├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── Src ├── Dc_Memory.c ├── Dc_Memory.h ├── Dc_Prodos.c ├── Dc_Prodos.h ├── Dc_Shared.c ├── Dc_Shared.h ├── File_AppleSingle.c ├── File_AppleSingle.h ├── Main.c ├── Prodos_Add.c ├── Prodos_Add.h ├── Prodos_Check.c ├── Prodos_Check.h ├── Prodos_Create.c ├── Prodos_Create.h ├── Prodos_Delete.c ├── Prodos_Delete.h ├── Prodos_Dump.c ├── Prodos_Dump.h ├── Prodos_Extract.c ├── Prodos_Extract.h ├── Prodos_Move.c ├── Prodos_Move.h ├── Prodos_Rename.c ├── Prodos_Rename.h ├── Prodos_Source.c ├── Prodos_Source.h ├── log.c ├── log.h └── os │ ├── os.c │ ├── os.h │ ├── posix.c │ └── win32.c └── cadius.pro /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /bin/ 3 | /build/ 4 | /cadius 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #### PROJECT SETTINGS #### 2 | # The name of the executable to be created 3 | BIN_NAME := cadius 4 | # Compiler used 5 | CC ?= gcc 6 | # Extension of source files used in the project 7 | SRC_EXT = c 8 | # Path to the source directory, relative to the makefile 9 | SRC_PATH = . 10 | # Space-separated pkg-config libraries used by this project 11 | LIBS = 12 | # General compiler flags 13 | COMPILE_FLAGS = -Wall -Wextra -O3 -g 14 | # Additional release-specific flags 15 | RCOMPILE_FLAGS = -D NDEBUG 16 | # Additional debug-specific flags 17 | DCOMPILE_FLAGS = -D DEBUG 18 | # Add additional include paths 19 | INCLUDES = -I $(SRC_PATH) 20 | # General linker settings 21 | LINK_FLAGS = 22 | # Additional release-specific linker settings 23 | RLINK_FLAGS = 24 | # Additional debug-specific linker settings 25 | DLINK_FLAGS = 26 | # Destination directory, like a jail or mounted system 27 | DESTDIR = / 28 | # Install path (bin/ is appended automatically) 29 | INSTALL_PREFIX = usr/local 30 | #### END PROJECT SETTINGS #### 31 | 32 | # Optionally you may move the section above to a separate config.mk file, and 33 | # uncomment the line below 34 | # include config.mk 35 | 36 | # Generally should not need to edit below this line 37 | 38 | # Obtains the OS type, either 'Darwin' (OS X) or 'Linux' 39 | UNAME_S:=$(shell uname -s) 40 | 41 | # Function used to check variables. Use on the command line: 42 | # make print-VARNAME 43 | # Useful for debugging and adding features 44 | print-%: ; @echo $*=$($*) 45 | 46 | # Shell used in this makefile 47 | # bash is used for 'echo -en' 48 | SHELL = /bin/bash 49 | # Clear built-in rules 50 | .SUFFIXES: 51 | # Programs for installation 52 | INSTALL = install 53 | INSTALL_PROGRAM = $(INSTALL) 54 | INSTALL_DATA = $(INSTALL) -m 644 55 | 56 | # Append pkg-config specific libraries if need be 57 | ifneq ($(LIBS),) 58 | COMPILE_FLAGS += $(shell pkg-config --cflags $(LIBS)) 59 | LINK_FLAGS += $(shell pkg-config --libs $(LIBS)) 60 | endif 61 | 62 | # Verbose option, to output compile and link commands 63 | export V := false 64 | export CMD_PREFIX := @ 65 | ifeq ($(V),true) 66 | CMD_PREFIX := 67 | endif 68 | 69 | # Combine compiler and linker flags 70 | release: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS) 71 | release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS) 72 | debug: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS) 73 | debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS) 74 | 75 | # Build and output paths 76 | release: export BUILD_PATH := build/release 77 | release: export BIN_PATH := bin/release 78 | debug: export BUILD_PATH := build/debug 79 | debug: export BIN_PATH := bin/debug 80 | install: export BIN_PATH := bin/release 81 | 82 | # Find all source files in the source directory, sorted by most 83 | # recently modified 84 | ifeq ($(UNAME_S),Darwin) 85 | SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-) 86 | else 87 | SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' -printf '%T@\t%p\n' \ 88 | | sort -k 1nr | cut -f2-) 89 | endif 90 | 91 | # fallback in case the above fails 92 | rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \ 93 | $(filter $(subst *,%,$2), $d)) 94 | ifeq ($(SOURCES),) 95 | SOURCES := $(call rwildcard, $(SRC_PATH), *.$(SRC_EXT)) 96 | endif 97 | 98 | # Set the object file names, with the source directory stripped 99 | # from the path, and the build path prepended in its place 100 | OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o) 101 | # Set the dependency files that will be used to add header dependencies 102 | DEPS = $(OBJECTS:.o=.d) 103 | 104 | # Macros for timing compilation 105 | ifeq ($(UNAME_S),Darwin) 106 | CUR_TIME = awk 'BEGIN{srand(); print srand()}' 107 | TIME_FILE = $(dir $@).$(notdir $@)_time 108 | START_TIME = $(CUR_TIME) > $(TIME_FILE) 109 | END_TIME = read st < $(TIME_FILE) ; \ 110 | $(RM) $(TIME_FILE) ; \ 111 | st=$$((`$(CUR_TIME)` - $$st)) ; \ 112 | echo $$st 113 | else 114 | TIME_FILE = $(dir $@).$(notdir $@)_time 115 | START_TIME = date '+%s' > $(TIME_FILE) 116 | END_TIME = read st < $(TIME_FILE) ; \ 117 | $(RM) $(TIME_FILE) ; \ 118 | st=$$((`date '+%s'` - $$st - 86400)) ; \ 119 | echo `date -u -d @$$st '+%H:%M:%S'` 120 | endif 121 | 122 | # Version macros 123 | # Comment/remove this section to remove versioning 124 | USE_VERSION := false 125 | # If this isn't a git repo or the repo has no tags, git describe will return non-zero 126 | ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0) 127 | USE_VERSION := true 128 | VERSION := $(shell git describe --tags --long --dirty --always | \ 129 | sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g') 130 | VERSION_MAJOR := $(word 1, $(VERSION)) 131 | VERSION_MINOR := $(word 2, $(VERSION)) 132 | VERSION_PATCH := $(word 3, $(VERSION)) 133 | VERSION_REVISION := $(word 4, $(VERSION)) 134 | VERSION_HASH := $(word 5, $(VERSION)) 135 | VERSION_STRING := \ 136 | "$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)" 137 | override CFLAGS := $(CFLAGS) \ 138 | -D VERSION_MAJOR=$(VERSION_MAJOR) \ 139 | -D VERSION_MINOR=$(VERSION_MINOR) \ 140 | -D VERSION_PATCH=$(VERSION_PATCH) \ 141 | -D VERSION_REVISION=$(VERSION_REVISION) \ 142 | -D VERSION_HASH=\"$(VERSION_HASH)\" 143 | endif 144 | 145 | # Standard, non-optimized release build 146 | .PHONY: release 147 | release: dirs 148 | ifeq ($(USE_VERSION), true) 149 | @echo "Beginning release build v$(VERSION_STRING)" 150 | else 151 | @echo "Beginning release build" 152 | endif 153 | @$(START_TIME) 154 | @$(MAKE) all --no-print-directory 155 | @echo -n "Total build time: " 156 | @$(END_TIME) 157 | 158 | # Debug build for gdb debugging 159 | .PHONY: debug 160 | debug: dirs 161 | ifeq ($(USE_VERSION), true) 162 | @echo "Beginning debug build v$(VERSION_STRING)" 163 | else 164 | @echo "Beginning debug build" 165 | endif 166 | @$(START_TIME) 167 | @$(MAKE) all --no-print-directory 168 | @echo -n "Total build time: " 169 | @$(END_TIME) 170 | 171 | # Create the directories used in the build 172 | .PHONY: dirs 173 | dirs: 174 | @echo "Creating directories" 175 | @mkdir -p $(dir $(OBJECTS)) 176 | @mkdir -p $(BIN_PATH) 177 | 178 | # Installs to the set path 179 | .PHONY: install 180 | install: 181 | @echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin" 182 | @$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin 183 | 184 | # Uninstalls the program 185 | .PHONY: uninstall 186 | uninstall: 187 | @echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)" 188 | @$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME) 189 | 190 | # Removes all build files 191 | .PHONY: clean 192 | clean: 193 | @echo "Deleting $(BIN_NAME) symlink" 194 | @$(RM) $(BIN_NAME) 195 | @echo "Deleting directories" 196 | @$(RM) -r build 197 | @$(RM) -r bin 198 | 199 | # Main rule, checks the executable and symlinks to the output 200 | all: $(BIN_PATH)/$(BIN_NAME) 201 | @echo "Making symlink: $(BIN_NAME) -> $<" 202 | @$(RM) $(BIN_NAME) 203 | @ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME) 204 | 205 | # Link the executable 206 | $(BIN_PATH)/$(BIN_NAME): $(OBJECTS) 207 | @echo "Linking: $@" 208 | @$(START_TIME) 209 | $(CMD_PREFIX)$(CC) $(OBJECTS) $(LDFLAGS) -o $@ 210 | @echo -en "\t Link time: " 211 | @$(END_TIME) 212 | 213 | # Add dependency files, if they exist 214 | -include $(DEPS) 215 | 216 | # Source file rules 217 | # After the first compilation they will be joined with the rules from the 218 | # dependency files to provide header dependencies 219 | $(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT) 220 | @echo "Compiling: $< -> $@" 221 | @$(START_TIME) 222 | $(CMD_PREFIX)$(CC) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@ 223 | @echo -en "\t Compile time: " 224 | @$(END_TIME) 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cadius 2 | 3 | BrutalDeluxe's Cadius disk imaging utility, with some maintenance and *nix support. 4 | 5 | ## Getting Started 6 | 7 | It is recommended that you begin by [reading the official documentation](http://brutaldeluxe.fr/products/crossdevtools/cadius/index.html). Prebuilt binaries coming soon, but for now you must build from source. 8 | 9 | View the contents of an image by using the `CATALOG` command: 10 | 11 | ```bash 12 | cadius CATALOG ~/path/to/image.po | less 13 | ``` 14 | 15 | ## Building 16 | 17 | This tutorial covers building for most *nix flavors, or on Windows with Cygwin. Windows instructions coming soon, but you should be able to add all of the files to a new MSVC project and build it. 18 | 19 | Ensure your system has `gcc` or `clang` and associated build tools, then clone the repository, and build it: 20 | 21 | ```bash 22 | git clone https://github.com/mach-kernel/cadius.git 23 | cd cadius 24 | # GCC is default, but override with CC=clang for clang or your preferred compiler 25 | make 26 | ./bin/release/cadius 27 | ``` 28 | 29 | ## Contributions 30 | 31 | Any and all contributions are welcome. Included is also a `cadius.pro` file you can use with [Qt Creator](http://doc.qt.io/qtcreator/) if you want an IDE with a nice GDB frontend. Be mindful of the following: 32 | 33 | - Preserve the existing code style, especially with pointer declarations. 34 | - As you explore the codebase, consider writing a Doxygen docstring for functions you come across / find important. 35 | - Try to test on OS X and Linux if including new headers. 36 | - In your PR, please add a changelog entry. 37 | 38 | ## Changelog 39 | 40 | #### 1.4.6 41 | - Fix ADDFILE erroneously making the first block of a file sparse (thanks [@inexorabletash](https://github.com/inexorabletash)). [#43](https://github.com/mach-kernel/cadius/pull/43) 42 | 43 | #### 1.4.5 44 | - Fix `os_GetFolderFiles` calloc too small. 45 | - AppleSingle check for OOB accesses (thanks [@peterferrie](https://github.com/peterferrie)). [#34](https://github.com/mach-kernel/cadius/pull/34) 46 | 47 | #### 1.4.4 48 | - Fix 140KiB volume size assertion (thanks [@inexorabletash](https://github.com/inexorabletash)): [#30](https://github.com/mach-kernel/cadius/pull/27) 49 | 50 | #### 1.4.3 51 | - `os_GetFolderFiles` segfault fixed. Thanks [@beevik](https://github.com/beevik): [#26](https://github.com/mach-kernel/cadius/pull/26)! 52 | - `-C | --no-case-bits` option added to `ADDFILE`, `ADDFOLDER`, `REPLACEFILE`, `CREATEVOLUME`, and `CREATEFOLDER` actions: [#27](https://github.com/mach-kernel/cadius/pull/27). 53 | - Fix warnings. 54 | 55 | #### 1.4.2 56 | - `CREATEVOLUME` did not previously allow for the creation of 143kb images. Thanks [@inexorabletash](https://github.com/inexorabletash)! 57 | 58 | #### 1.4.1 59 | - `REPLACEFILE` 60 | - Buffer overflow resolved [#17](https://github.com/mach-kernel/cadius/issues/17). Thanks [@sicklittlemonkey](https://github.com/sicklittlemonkey)! 61 | - Error on replacing a file that does not exist silenced [#16](https://github.com/mach-kernel/cadius/issues/16). 62 | - Typo fixed. Thanks [@a2sheppy](https://github.com/a2sheppy) 63 | - `--quiet` command line argument added! 64 | 65 | #### 1.4.0 66 | - Adds AppleSingle file format support, with initial support for data and ProDOS file info IDs 1 & 11 ([#7](https://github.com/mach-kernel/cadius/issues/7)). 67 | - Fix path bugs on Windows ([#9](https://github.com/mach-kernel/cadius/issues/9)). 68 | - Fix buffer overflow from [#12](https://github.com/mach-kernel/cadius/issues/12). 69 | - Fix segfault when using `EXTRACTVOLUME`. 70 | 71 | A big thank you to [@oliverschmidt](https://github.com/oliverschmidt) for helping test this release! 72 | 73 | #### 1.3.2 74 | - Maintenance release / macro cleanup. 75 | 76 | #### 1.3.1 77 | - Resolves timestamp bugs in [#10](https://github.com/mach-kernel/cadius/issues/10). Thanks, @a2-4am! 78 | 79 | #### 1.3.0 80 | - Adds ability to specify a file's `type` and `auxtype` via the filename, resolving [#4](https://github.com/mach-kernel/cadius/issues/4). A big thank you to @JohnMBrooks for testing this PR! 81 | - `REPLACEFILE` command, `DELETEFILE` support for type/auxtype suffix. 82 | 83 | #### 1.2-b3 84 | - Fix Windows build issues, make some shared OS methods static / remove from `os.c` ([@mach-kernel](https://github.com/mach-kernel)). 85 | 86 | #### 1.2-b2 87 | - Clean up OS macros, explicit `win32` and `posix` modules ([@mach-kernel](https://github.com/mach-kernel)). 88 | 89 | #### 1.2-b1 90 | - UTF-8 encode all source files. 91 | - Initial POSIX support ([@mach-kernel](https://github.com/mach-kernel)). 92 | 93 | #### 1.1 94 | - Initial fork from BrutalDeluxe. 95 | 96 | ## License 97 | 98 | CADIUS was developed by [BrutalDeluxe](http://brutaldeluxe.fr). All contributions licensed under the [GNU/GPL](https://github.com/mach-kernel/cadius/blob/master/LICENSE) and attributed to contributors. All prior / existing code attributed under the original license. [GenericMakefile](https://github.com/mbcrawfo/GenericMakefile) is licensed under the [MIT License](https://github.com/mbcrawfo/GenericMakefile/blob/master/LICENSE). 99 | -------------------------------------------------------------------------------- /Src/Dc_Memory.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Dc_Memory.c : Module pour la bibliothèque de gestion de la mémoire. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #if IS_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include 18 | 19 | #include "os/os.h" 20 | #include "Dc_Shared.h" 21 | #include "Dc_Prodos.h" 22 | #include "Dc_Memory.h" 23 | 24 | /***************************************************/ 25 | /* my_Memory() : Gestion des ressources mémoire. */ 26 | /***************************************************/ 27 | void my_Memory(int code, void *data, void *value) 28 | { 29 | int i, index; 30 | char *path; 31 | char *message; 32 | static int nb_entry; 33 | static struct file_descriptive_entry *first_entry; 34 | static struct file_descriptive_entry *last_entry; 35 | static struct file_descriptive_entry **tab_entry; 36 | struct file_descriptive_entry *current_entry; 37 | struct file_descriptive_entry *next_entry; 38 | struct file_descriptive_entry *delete_entry; 39 | static int nb_directory; 40 | static struct file_descriptive_entry *first_directory; 41 | static struct file_descriptive_entry *last_directory; 42 | static struct file_descriptive_entry **tab_directory; 43 | struct file_descriptive_entry *current_directory; 44 | struct file_descriptive_entry *next_directory; 45 | struct file_descriptive_entry *delete_directory; 46 | static int nb_filepath; 47 | static struct file_path *first_filepath; 48 | static struct file_path *last_filepath; 49 | struct file_path *current_filepath; 50 | struct file_path *next_filepath; 51 | static int nb_error; 52 | static struct error *first_error; 53 | static struct error *last_error; 54 | struct error *current_error; 55 | struct error *next_error; 56 | 57 | switch(code) 58 | { 59 | case MEMORY_INIT : 60 | nb_entry = 0; 61 | first_entry = NULL; 62 | last_entry = NULL; 63 | tab_entry = NULL; 64 | nb_directory = 0; 65 | first_directory = NULL; 66 | last_directory = NULL; 67 | tab_directory = NULL; 68 | nb_filepath = 0; 69 | first_filepath = NULL; 70 | last_filepath = NULL; 71 | nb_error = 0; 72 | first_error = NULL; 73 | last_error = NULL; 74 | break; 75 | 76 | case MEMORY_FREE : 77 | my_Memory(MEMORY_FREE_ENTRY,NULL,NULL); 78 | my_Memory(MEMORY_FREE_DIRECTORY,NULL,NULL); 79 | my_Memory(MEMORY_FREE_FILE,NULL,NULL); 80 | my_Memory(MEMORY_FREE_ERROR,NULL,NULL); 81 | break; 82 | 83 | /*********************************************/ 84 | /* Liste chainée des structure entry : File */ 85 | /*********************************************/ 86 | case MEMORY_ADD_ENTRY : 87 | current_entry = (struct file_descriptive_entry *) data; 88 | if(current_entry == NULL) 89 | return; 90 | 91 | /* Ajoute à la fin de la liste */ 92 | if(first_entry == NULL) 93 | first_entry = current_entry; 94 | else 95 | last_entry->next = current_entry; 96 | last_entry = current_entry; 97 | nb_entry++; 98 | break; 99 | 100 | case MEMORY_GET_ENTRY_NB : 101 | *((int *)data) = nb_entry; 102 | break; 103 | 104 | case MEMORY_GET_ENTRY : 105 | index = *((int *) data); 106 | *((struct file_descriptive_entry **) value) = NULL; 107 | if(index <= 0 || index > nb_entry) 108 | return; 109 | 110 | /* Recherche le index-nth entry */ 111 | for(i=1, current_entry = first_entry; inext; 113 | *((struct file_descriptive_entry **) value) = current_entry; 114 | break; 115 | 116 | case MEMORY_BUILD_ENTRY_TAB : 117 | /* Libération */ 118 | if(tab_entry) 119 | free(tab_entry); 120 | 121 | /* Allocation */ 122 | tab_entry = (struct file_descriptive_entry **) calloc(1+nb_entry,sizeof(struct file_descriptive_entry *)); 123 | if(tab_entry == NULL) 124 | break; 125 | 126 | /* Remplissage */ 127 | for(i=0, current_entry = first_entry; current_entry; i++,current_entry=current_entry->next) 128 | tab_entry[i] = current_entry; 129 | break; 130 | 131 | case MEMORY_REMOVE_ENTRY : 132 | delete_entry = (struct file_descriptive_entry *) data; 133 | if(delete_entry == NULL) 134 | break; 135 | 136 | /* Cas particulier : 1 seule structure */ 137 | if(nb_entry == 1 && first_entry == delete_entry) 138 | { 139 | first_entry = NULL; 140 | last_entry = NULL; 141 | nb_entry = 0; 142 | } 143 | else if(first_entry == delete_entry) 144 | { 145 | /* En 1ère position */ 146 | first_entry = first_entry->next; 147 | memmove(&tab_entry[0],&tab_entry[1],(nb_entry-1)*sizeof(struct file_descriptive_entry *)); 148 | nb_entry--; 149 | } 150 | else if(last_entry == delete_entry) 151 | { 152 | /* En dernière position */ 153 | tab_entry[nb_entry-2]->next = NULL; 154 | last_entry = tab_entry[nb_entry-2]; 155 | nb_entry--; 156 | } 157 | else 158 | { 159 | /* Au milieu */ 160 | for(i=0; inext = tab_entry[i]->next; 164 | memmove(&tab_entry[i],&tab_entry[i+1],(nb_entry-i-1)*sizeof(struct file_descriptive_entry *)); 165 | nb_entry--; 166 | break; 167 | } 168 | } 169 | break; 170 | 171 | case MEMORY_FREE_ENTRY : 172 | for(current_entry = first_entry; current_entry; ) 173 | { 174 | next_entry = current_entry->next; 175 | mem_free_entry(current_entry); 176 | current_entry = next_entry; 177 | } 178 | if(tab_entry) 179 | free(tab_entry); 180 | nb_entry = 0; 181 | first_entry = NULL; 182 | last_entry = NULL; 183 | tab_entry = NULL; 184 | break; 185 | 186 | /***********************************************/ 187 | /* Liste chainée des structure entry : SubDir */ 188 | /***********************************************/ 189 | case MEMORY_ADD_DIRECTORY : 190 | current_directory = (struct file_descriptive_entry *) data; 191 | if(current_directory == NULL) 192 | return; 193 | 194 | /* Ajoute à la fin de la liste */ 195 | if(first_directory == NULL) 196 | first_directory = current_directory; 197 | else 198 | last_directory->next = current_directory; 199 | last_directory = current_directory; 200 | nb_directory++; 201 | break; 202 | 203 | case MEMORY_GET_DIRECTORY_NB : 204 | *((int *)data) = nb_directory; 205 | break; 206 | 207 | case MEMORY_GET_DIRECTORY : 208 | index = *((int *) data); 209 | *((struct file_descriptive_entry **) value) = NULL; 210 | if(index <= 0 || index > nb_directory) 211 | return; 212 | 213 | /* Recherche le index-nth directory */ 214 | for(i=1, current_directory = first_directory; inext; 216 | *((struct file_descriptive_entry **) value) = current_directory; 217 | break; 218 | 219 | case MEMORY_BUILD_DIRECTORY_TAB : 220 | /* Libération */ 221 | if(tab_directory) 222 | free(tab_directory); 223 | 224 | /* Allocation */ 225 | tab_directory = (struct file_descriptive_entry **) calloc(1+nb_directory,sizeof(struct file_descriptive_entry *)); 226 | if(tab_directory == NULL) 227 | break; 228 | 229 | /* Remplissage */ 230 | for(i=0, current_directory = first_directory; current_directory; i++,current_directory=current_directory->next) 231 | tab_directory[i] = current_directory; 232 | break; 233 | 234 | case MEMORY_REMOVE_DIRECTORY : 235 | delete_directory = (struct file_descriptive_entry *) data; 236 | if(delete_directory == NULL) 237 | break; 238 | 239 | /* Cas particulier : 1 seule structure */ 240 | if(nb_directory == 1 && first_directory == delete_directory) 241 | { 242 | first_directory = NULL; 243 | last_directory = NULL; 244 | nb_directory = 0; 245 | } 246 | else if(first_directory == delete_directory) 247 | { 248 | /* En 1ère position */ 249 | first_directory = first_directory->next; 250 | memmove(&tab_directory[0],&tab_directory[1],(nb_directory-1)*sizeof(struct file_descriptive_entry *)); 251 | nb_directory--; 252 | } 253 | else if(last_directory == delete_directory) 254 | { 255 | /* En dernière position */ 256 | tab_directory[nb_directory-2]->next = NULL; 257 | last_directory = tab_directory[nb_directory-2]; 258 | nb_directory--; 259 | } 260 | else 261 | { 262 | /* Au milieu */ 263 | for(i=0; inext = tab_directory[i]->next; 267 | memmove(&tab_directory[i],&tab_directory[i+1],(nb_directory-i-1)*sizeof(struct file_descriptive_entry *)); 268 | nb_directory--; 269 | break; 270 | } 271 | } 272 | break; 273 | 274 | case MEMORY_FREE_DIRECTORY : 275 | for(current_directory = first_directory; current_directory; ) 276 | { 277 | next_directory = current_directory->next; 278 | mem_free_entry(current_directory); 279 | current_directory = next_directory; 280 | } 281 | if(tab_directory) 282 | free(tab_directory); 283 | nb_directory = 0; 284 | first_directory = NULL; 285 | last_directory = NULL; 286 | tab_directory = NULL; 287 | break; 288 | 289 | /*******************************************/ 290 | /* Liste chainée des structure file_path */ 291 | /*******************************************/ 292 | case MEMORY_ADD_FILE : 293 | path = (char *) data; 294 | 295 | /* Allocation mémoire */ 296 | current_filepath = (struct file_path *) calloc(1,sizeof(struct file_path)); 297 | if(current_filepath == NULL) 298 | return; 299 | 300 | current_filepath->path = my_strdup(path); 301 | 302 | if(current_filepath->path == NULL) 303 | { 304 | free(current_filepath); 305 | return; 306 | } 307 | 308 | /* Ajoute à la fin de la liste */ 309 | if(first_filepath == NULL) 310 | first_filepath = current_filepath; 311 | else 312 | last_filepath->next = current_filepath; 313 | last_filepath = current_filepath; 314 | nb_filepath++; 315 | break; 316 | 317 | case MEMORY_GET_FILE_NB : 318 | *((int *)data) = nb_filepath; 319 | break; 320 | 321 | case MEMORY_GET_FILE : 322 | index = *((int *) data); 323 | *((struct file_path **) value) = NULL; 324 | if(index <= 0 || index > nb_filepath) 325 | return; 326 | 327 | /* Recherche le index-nth entry */ 328 | for(i=1, current_filepath = first_filepath; inext; 330 | *((char **) value) = current_filepath->path; 331 | break; 332 | 333 | case MEMORY_FREE_FILE : 334 | for(current_filepath = first_filepath; current_filepath; ) 335 | { 336 | next_filepath = current_filepath->next; 337 | mem_free_filepath(current_filepath); 338 | current_filepath = next_filepath; 339 | } 340 | nb_filepath = 0; 341 | first_filepath = NULL; 342 | last_filepath = NULL; 343 | break; 344 | 345 | /***************************************/ 346 | /* Liste chainée des structure error */ 347 | /***************************************/ 348 | case MEMORY_ADD_ERROR : 349 | message = (char *) data; 350 | 351 | /* Allocation mémoire */ 352 | current_error = (struct error *) calloc(1,sizeof(struct error)); 353 | if(current_error == NULL) 354 | return; 355 | current_error->message = my_strdup(message); 356 | if(current_error->message == NULL) 357 | { 358 | free(current_error); 359 | return; 360 | } 361 | 362 | /* Ajoute à la fin de la liste */ 363 | if(first_error == NULL) 364 | first_error = current_error; 365 | else 366 | last_error->next = current_error; 367 | last_error = current_error; 368 | nb_error++; 369 | break; 370 | 371 | case MEMORY_GET_ERROR_NB : 372 | *((int *)data) = nb_error; 373 | break; 374 | 375 | case MEMORY_GET_ERROR : 376 | index = *((int *) data); 377 | *((struct error **) value) = NULL; 378 | if(index <= 0 || index > nb_error) 379 | return; 380 | 381 | /* Recherche le index-nth entry */ 382 | for(i=1, current_error = first_error; inext; 384 | *((struct error **) value) = current_error; 385 | break; 386 | 387 | case MEMORY_FREE_ERROR : 388 | for(current_error = first_error; current_error; ) 389 | { 390 | next_error = current_error->next; 391 | if(current_error->message) 392 | free(current_error->message); 393 | free(current_error); 394 | current_error = next_error; 395 | } 396 | nb_error = 0; 397 | first_error = NULL; 398 | last_error = NULL; 399 | break; 400 | 401 | default : 402 | break; 403 | } 404 | } 405 | 406 | 407 | /**********************************************************/ 408 | /* mem_free_param() : Libération de la structure Param. */ 409 | /**********************************************************/ 410 | void mem_free_param(struct parameter *param) 411 | { 412 | if(param) 413 | { 414 | if(param->image_file_path) 415 | free(param->image_file_path); 416 | 417 | if(param->prodos_file_path) 418 | free(param->prodos_file_path); 419 | 420 | if(param->prodos_folder_path) 421 | free(param->prodos_folder_path); 422 | 423 | if(param->output_directory_path) 424 | free(param->output_directory_path); 425 | 426 | if(param->file_path) 427 | free(param->file_path); 428 | 429 | if(param->folder_path) 430 | free(param->folder_path); 431 | 432 | if(param->new_file_name) 433 | free(param->new_file_name); 434 | 435 | if(param->new_folder_name) 436 | free(param->new_folder_name); 437 | 438 | if(param->new_volume_name) 439 | free(param->new_volume_name); 440 | 441 | if(param->new_file_path) 442 | free(param->new_file_path); 443 | 444 | if(param->new_folder_path) 445 | free(param->new_folder_path); 446 | 447 | free(param); 448 | } 449 | } 450 | 451 | /***********************************************************************/ 452 | -------------------------------------------------------------------------------- /Src/Dc_Memory.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Dc_Memory.h : Header pour la bibliothèque de gestion de la mémoire. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #define MEMORY_INIT 10 14 | #define MEMORY_FREE 11 15 | 16 | #define MEMORY_ADD_ENTRY 20 17 | #define MEMORY_GET_ENTRY_NB 21 18 | #define MEMORY_GET_ENTRY 22 19 | #define MEMORY_BUILD_ENTRY_TAB 23 20 | #define MEMORY_REMOVE_ENTRY 24 21 | #define MEMORY_FREE_ENTRY 25 22 | 23 | #define MEMORY_ADD_DIRECTORY 30 24 | #define MEMORY_GET_DIRECTORY_NB 31 25 | #define MEMORY_GET_DIRECTORY 32 26 | #define MEMORY_BUILD_DIRECTORY_TAB 33 27 | #define MEMORY_REMOVE_DIRECTORY 34 28 | #define MEMORY_FREE_DIRECTORY 35 29 | 30 | #define MEMORY_ADD_FILE 40 31 | #define MEMORY_GET_FILE_NB 41 32 | #define MEMORY_GET_FILE 42 33 | #define MEMORY_FREE_FILE 43 34 | 35 | #define MEMORY_ADD_ERROR 50 36 | #define MEMORY_GET_ERROR_NB 51 37 | #define MEMORY_GET_ERROR 52 38 | #define MEMORY_FREE_ERROR 53 39 | 40 | struct parameter 41 | { 42 | int action; 43 | 44 | char *image_file_path; 45 | char *prodos_file_path; 46 | char *prodos_folder_path; 47 | char *output_directory_path; 48 | 49 | char *new_file_name; 50 | char *new_folder_name; 51 | char *new_volume_name; 52 | 53 | char *new_file_path; 54 | char *new_folder_path; 55 | 56 | int new_volume_size_kb; 57 | 58 | char *file_path; 59 | char *folder_path; 60 | 61 | int verbose; 62 | bool output_apple_single; 63 | bool zero_case_bits; 64 | }; 65 | 66 | struct error 67 | { 68 | char *message; 69 | 70 | struct error *next; 71 | }; 72 | 73 | void my_Memory(int,void *,void *); 74 | void mem_free_param(struct parameter *); 75 | 76 | /***********************************************************************/ -------------------------------------------------------------------------------- /Src/Dc_Prodos.h: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /* */ 3 | /* Dc_Prodos.h : Header pour la bibliothèque de gestion du Prodos. */ 4 | /* */ 5 | /**********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /**********************************************************************/ 8 | 9 | #pragma once 10 | 11 | #define IMG_HEADER_SIZE 0x40 /* 2MG Header Size */ 12 | #define HDV_HEADER_SIZE 0x00 /* HDV Header Size */ 13 | #define PO_HEADER_SIZE 0x00 /* PO Header Size */ 14 | 15 | #define IMAGE_UNKNOWN 0 16 | #define IMAGE_2MG 1 /* 2MG */ 17 | #define IMAGE_HDV 2 /* HDV */ 18 | #define IMAGE_PO 3 /* PO */ 19 | 20 | #define BLOCK_SIZE 512 /* Taille d'un block */ 21 | #define INDEX_PER_BLOCK 256 /* Nombre d'index de block dans un block */ 22 | 23 | #define UPDATE_ADD 1 24 | #define UPDATE_REMOVE 2 25 | 26 | #define TYPE_ENTRY_SEEDLING 1 27 | #define TYPE_ENTRY_SAPLING 2 28 | #define TYPE_ENTRY_TREE 3 29 | #define TYPE_ENTRY_EXTENDED 5 /* Data + Resource */ 30 | 31 | struct prodos_date 32 | { 33 | int year; 34 | int month; 35 | int day; 36 | 37 | char ascii[20]; 38 | }; 39 | 40 | struct prodos_time 41 | { 42 | int hour; 43 | int minute; 44 | 45 | char ascii[20]; 46 | }; 47 | 48 | #define BLOCK_TYPE_EMPTY 0 /* Par défaut */ 49 | #define BLOCK_TYPE_BOOT 1 50 | #define BLOCK_TYPE_VOLUME 2 51 | #define BLOCK_TYPE_BITMAP 3 52 | #define BLOCK_TYPE_FILE 4 53 | #define BLOCK_TYPE_FOLDER 5 54 | 55 | struct prodos_image 56 | { 57 | char *image_file_path; 58 | 59 | int image_format; /* 2mg, hdv, po*/ 60 | int image_header_size; 61 | 62 | int image_length; 63 | unsigned char *image_data; 64 | int nb_block; 65 | int nb_free_block; 66 | 67 | unsigned char *block_modified; /* Tableau des blocks modifiés */ 68 | 69 | struct volume_directory_header *volume_header; 70 | 71 | /* Version integer de la bitmap */ 72 | int *block_allocation_table; 73 | 74 | int *block_usage_type; /* Type de données de chaque bloc (pour le CHECK_VOLUME) */ 75 | void **block_usage_object; /* Objet lié à chaque bloc (pour le CHECK_VOLUME) */ 76 | 77 | /** Fichiers et répertoires attachés au Volume Directory **/ 78 | int nb_file; /* Liste des fichiers de ce répertoire */ 79 | struct file_descriptive_entry **tab_file; 80 | 81 | int nb_directory; /* Liste des répertoires de ce répertoire */ 82 | struct file_descriptive_entry **tab_directory; 83 | 84 | /** Statistiques **/ 85 | int nb_extract_file; 86 | int nb_extract_folder; 87 | int nb_extract_error; 88 | 89 | int nb_add_file; 90 | int nb_add_folder; 91 | int nb_add_error; 92 | }; 93 | 94 | #define VOLUME_STORAGETYPE_OFFSET 0x04 95 | #define VOLUME_LOWERCASE_OFFSET 0x1A 96 | #define VOLUME_NAME_OFFSET 0x05 97 | #define VOLUME_DATEMODIF_OFFSET 0x16 98 | #define VOLUME_TIMEMODIF_OFFSET 0x18 99 | #define VOLUME_ENTRYLENGTH_OFFSET 0x23 100 | #define VOLUME_ENTRIESPERBLOCK_OFFSET 0x24 101 | #define VOLUME_FILECOUNT_OFFSET 0x25 102 | 103 | struct volume_directory_header 104 | { 105 | int previous_block; /* Tjs à zéro */ 106 | int next_block; 107 | 108 | BYTE storage_type; 109 | char storage_type_ascii[50]; 110 | char storage_type_ascii_short[10]; 111 | 112 | int name_length; 113 | char volume_name[16]; 114 | char volume_name_case[16]; 115 | WORD lowercase; /* GS/OS */ 116 | 117 | struct prodos_date volume_creation_date; 118 | struct prodos_time volume_creation_time; 119 | 120 | struct prodos_date volume_modification_date; /* GS/OS */ 121 | struct prodos_time volume_modification_time; 122 | 123 | int version_formatted; 124 | int min_version; 125 | 126 | BYTE access; 127 | char access_ascii[50]; 128 | 129 | int entry_length; 130 | int entries_per_block; 131 | int file_subdir_count; 132 | 133 | int bitmap_block; 134 | int total_blocks; 135 | 136 | int struct_size; 137 | }; 138 | 139 | #define DIRECTORY_STORAGETYPE_OFFSET 0x04 140 | #define DIRECTORY_NAME_OFFSET 0x05 141 | #define DIRECTORY_LOWERCASE_OFFSET 0x20 142 | #define DIRECTORY_ENTRYLENGTH_OFFSET 0x23 143 | #define DIRECTORY_ENTRIESPERBLOCK_OFFSET 0x24 144 | #define DIRECTORY_FILECOUNT_OFFSET 0x25 145 | #define DIRECTORY_PARENTPOINTERBLOCK_OFFSET 0x27 146 | #define DIRECTORY_PARENTENTRY_OFFSET 0x29 147 | 148 | struct sub_directory_header 149 | { 150 | int previous_block; 151 | int next_block; 152 | 153 | BYTE storage_type; 154 | char storage_type_ascii[50]; 155 | char storage_type_ascii_short[10]; 156 | 157 | int name_length; 158 | char subdir_name[16]; 159 | char subdir_name_case[16]; 160 | WORD lowercase; /* GS/OS */ 161 | 162 | struct prodos_date subdir_creation_date; 163 | struct prodos_time subdir_creation_time; 164 | 165 | int version_created; 166 | int min_version; 167 | 168 | BYTE access; 169 | char access_ascii[50]; 170 | 171 | int entry_length; /* Constante : 27 */ 172 | int entries_per_block; /* Constante : 0D */ 173 | int file_count; /* Nombre de fichiers non supprimés */ 174 | int parent_pointer_block; /* Block (Volume Header ou Sub Dir) contenant l'entrée de ce SubDir */ 175 | int parent_entry; /* Indice 1->D de cette entrée dans le Dir Parent */ 176 | int parent_entry_length; 177 | 178 | int struct_size; 179 | }; 180 | 181 | #define FILE_STORAGETYPE_OFFSET 0x00 182 | #define FILE_NAME_OFFSET 0x01 183 | #define FILE_LOWERCASE_OFFSET 0x1C 184 | #define FILE_HEADERPOINTER_OFFSET 0x25 185 | 186 | struct file_descriptive_entry 187 | { 188 | BYTE storage_type; 189 | char storage_type_ascii[50]; 190 | char storage_type_ascii_short[10]; 191 | 192 | int name_length; 193 | char file_name[16]; 194 | char file_name_case[16]; 195 | WORD lowercase; /* GS/OS */ 196 | 197 | char *file_path; /* Chemin du ficher dans l'image */ 198 | 199 | BYTE file_type; 200 | WORD file_aux_type; 201 | char file_type_ascii[50]; 202 | 203 | int key_pointer_block; /* Block contenant les données */ 204 | int blocks_used; 205 | int eof_location; /* Taille du fichier en Byte */ 206 | 207 | struct prodos_date file_creation_date; 208 | struct prodos_time file_creation_time; 209 | 210 | struct prodos_date file_modification_date; 211 | struct prodos_time file_modification_time; 212 | 213 | int version_created; 214 | int min_version; 215 | 216 | BYTE access; 217 | char access_ascii[50]; 218 | 219 | /* Répartition des données du fichier */ 220 | int data_size; 221 | int data_block; 222 | int resource_size; 223 | int resource_block; 224 | 225 | int index_block; /* Nombre de bloc utilisés pour les index */ 226 | int nb_sparse; /* Nombre de bloc fantome */ 227 | 228 | int nb_used_block; /* Nombre de blocs utilisés par le fichier (index+data+resource) */ 229 | int *tab_used_block; /* Tableau des numéros de bloc */ 230 | 231 | int header_pointer_block; /* Block du SubDir décrivant cette entrée */ 232 | 233 | int struct_size; /* Taille de la structure ODS */ 234 | int depth; /* Profondeur de cette entrée dans l'arbre des fichiers : 1->N */ 235 | int block_location; /* Numéro du block (d'un directory) où cette entrée est décrite */ 236 | int entry_offset; /* Offset en byte par rapport au début du bloc de cette entrée File */ 237 | int processed; /* Entrée déjà traitée */ 238 | 239 | struct file_descriptive_entry *parent_directory; /* Entrée Parent Sub Directory */ 240 | 241 | int nb_file; /* Liste des fichiers de ce répertoire */ 242 | struct file_descriptive_entry **tab_file; 243 | 244 | int nb_directory; /* Liste des répertoires de ce répertoire */ 245 | struct file_descriptive_entry **tab_directory; 246 | 247 | int delete_folder_depth; /* Ce répertoire doit être supprimé (niveau de profondeur) */ 248 | 249 | struct file_descriptive_entry *next; 250 | }; 251 | 252 | struct prodos_file 253 | { 254 | int entry_type; /* Seedling, Sapling, Tree, Extended */ 255 | int entry_disk_block; /* Nombre de blocks pour stocker cette entrée (data+resource+index) */ 256 | int nb_data_block; 257 | int *tab_data_block; /* Liste des block alloués pour les data (permet une libération si pb) */ 258 | int nb_resource_block; 259 | int *tab_resource_block; /* Liste des block alloués pour les resource (permet une libération si pb) */ 260 | 261 | int data_length; 262 | unsigned char *data; 263 | int type_data; /* Seedling, Sapling, Tree */ 264 | int block_data; /* Nb de blocks utilisés pour stocker les data en mémoire */ 265 | int block_disk_data; /* Nb de blocks utilisés sur le disk pour stocker les data */ 266 | int empty_data; /* Tout est à zéro */ 267 | int index_data; /* Nb de blocks utilisés sur le disk pour stocker les index des data */ 268 | 269 | int has_resource; 270 | int resource_length; 271 | unsigned char *resource; 272 | int type_resource; /* Seedling, Sapling, Tree */ 273 | int block_resource; /* Nb de blocks utilisés pour stocker les resources en mémoire */ 274 | int block_disk_resource; /* Nb de blocks utilisés sur le disk pour stocker les resources */ 275 | int empty_resource; /* Tout est à zéro */ 276 | int index_resource; /* Nb de blocks utilisés sur le disk pour stocker les index des resources */ 277 | 278 | unsigned char resource_finderinfo_1[18]; 279 | unsigned char resource_finderinfo_2[18]; 280 | 281 | unsigned char type; 282 | WORD aux_type; 283 | unsigned char version_create; 284 | unsigned char min_version; 285 | unsigned char access; 286 | 287 | WORD file_creation_date; 288 | WORD file_creation_time; 289 | WORD file_modification_date; 290 | WORD file_modification_time; 291 | 292 | char *file_name_case; 293 | char *file_name; /* Majuscule */ 294 | WORD name_case; 295 | 296 | struct file_descriptive_entry *entry; 297 | }; 298 | 299 | struct prodos_image *LoadProdosImage(char *); 300 | struct file_descriptive_entry *ODSReadFileDescriptiveEntry(struct prodos_image *,char *,unsigned char *); 301 | int UpdateProdosImage(struct prodos_image *); 302 | struct file_descriptive_entry *GetProdosFile(struct prodos_image *,char *); 303 | struct file_descriptive_entry *GetProdosFolder(struct prodos_image *,char *,int); 304 | int *GetEntryBlock(struct prodos_image *,int,int,int,int *,int **,int *); 305 | int GetDataFile(struct prodos_image *,struct file_descriptive_entry *,struct prodos_file *); 306 | void GetBlockData(struct prodos_image *,int,unsigned char *); 307 | void SetBlockData(struct prodos_image *,int,unsigned char *); 308 | void GetProdosDate(WORD,struct prodos_date *); 309 | void GetProdosTime(WORD,struct prodos_time *); 310 | WORD BuildProdosDate(int,int,int); 311 | WORD BuildProdosTime(int,int); 312 | WORD BuildProdosCase(char *); 313 | int CheckProdosName(char *); 314 | void GetCurrentDate(WORD *,WORD *); 315 | int *AllocateImageBlock(struct prodos_image *,int); 316 | int AllocateFolderEntry(struct prodos_image *,struct file_descriptive_entry *,WORD *, BYTE *,WORD *); 317 | int UpdateEntryTable(int,int *,struct file_descriptive_entry ***,struct file_descriptive_entry *); 318 | int compare_entry(const void *,const void *); 319 | void mem_free_image(struct prodos_image *); 320 | void mem_free_entry(struct file_descriptive_entry *); 321 | void mem_free_file(struct prodos_file *); 322 | 323 | /***********************************************************************/ 324 | -------------------------------------------------------------------------------- /Src/Dc_Shared.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Dc_Shared.c : Module pour la bibliothèque de fonctions génériques. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Janv 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "Dc_Shared.h" 15 | #include "Dc_Memory.h" 16 | #include "File_AppleSingle.h" 17 | #include "os/os.h" 18 | #include "log.h" 19 | 20 | #ifdef IS_WINDOWS 21 | #include 22 | #endif 23 | 24 | /***************************************************************/ 25 | /* LoadBinaryFile() : Récupération des données d'un fichier. */ 26 | /***************************************************************/ 27 | unsigned char *LoadBinaryFile(char *file_path, int *data_length_rtn) 28 | { 29 | FILE *fd; 30 | int nb_read, file_size; 31 | unsigned char *data; 32 | 33 | /* Init */ 34 | *data_length_rtn = 0; 35 | 36 | /* Ouverture du fichier */ 37 | fd = fopen(file_path,"rb"); 38 | if(fd == NULL) 39 | return(NULL); 40 | 41 | /* Taille du fichier */ 42 | fseek(fd,0L,SEEK_END); 43 | file_size = ftell(fd); 44 | fseek(fd,0L,SEEK_SET); 45 | 46 | /* Allocation mémoire */ 47 | data = (unsigned char *) calloc(1,file_size+1); 48 | if(data == NULL) 49 | { 50 | fclose(fd); 51 | return(NULL); 52 | } 53 | 54 | /* Lecture des données */ 55 | nb_read = fread(data,1,file_size,fd); 56 | if(nb_read != file_size) 57 | { 58 | free(data); 59 | fclose(fd); 60 | return(NULL); 61 | } 62 | data[nb_read] = '\0'; 63 | 64 | /* Fermeture du fichier */ 65 | fclose(fd); 66 | 67 | /* Renvoi les données et la taille */ 68 | *data_length_rtn = nb_read; 69 | 70 | return(data); 71 | } 72 | 73 | 74 | /*************************************************************/ 75 | /* LoadTextFile() : Récupération des données d'un fichier. */ 76 | /*************************************************************/ 77 | unsigned char *LoadTextFile(char *file_path, int *data_length_rtn) 78 | { 79 | FILE *fd; 80 | int nb_read, file_size; 81 | unsigned char *data; 82 | 83 | /* Ouverture du fichier */ 84 | fd = fopen(file_path,"r"); 85 | if(fd == NULL) 86 | return(NULL); 87 | 88 | /* Taille du fichier */ 89 | fseek(fd,0L,SEEK_END); 90 | file_size = ftell(fd); 91 | fseek(fd,0L,SEEK_SET); 92 | 93 | /* Allocation mémoire */ 94 | data = calloc(1,file_size+10); 95 | if(data == NULL) 96 | { 97 | fclose(fd); 98 | return(NULL); 99 | } 100 | 101 | /* Lecture des données */ 102 | nb_read = fread(data,1,file_size,fd); 103 | if(nb_read < 0) 104 | { 105 | free(data); 106 | fclose(fd); 107 | return(NULL); 108 | } 109 | data[nb_read] = '\0'; 110 | 111 | /* Fermeture du fichier */ 112 | fclose(fd); 113 | 114 | /* Renvoi les données et la taille */ 115 | *data_length_rtn = nb_read; 116 | return(data); 117 | } 118 | 119 | 120 | /*************************************************************/ 121 | /* Get24bitValue() : Décode une valeur codée sur 3 octets. */ 122 | /*************************************************************/ 123 | int Get24bitValue(unsigned char *data, int offset) 124 | { 125 | return(data[offset] + 256*data[offset+1] + 65536*data[offset+2]); 126 | } 127 | 128 | 129 | /************************************************************/ 130 | /* GetWordValue() : Décode une valeur codée sur 2 octets. */ 131 | /************************************************************/ 132 | int GetWordValue(unsigned char *data, int offset) 133 | { 134 | return(data[offset] + 256*data[offset+1]); 135 | } 136 | 137 | 138 | /***********************************************************/ 139 | /* GetByteValue() : Décode une valeur codée sur 1 octet. */ 140 | /***********************************************************/ 141 | int GetByteValue(unsigned char *data, int offset) 142 | { 143 | return(data[offset]); 144 | } 145 | 146 | 147 | /***********************************************************/ 148 | /* SetWordValue() : Place une valeur codée sur 2 octets. */ 149 | /***********************************************************/ 150 | void SetWordValue(unsigned char *data, int offset, WORD value) 151 | { 152 | data[offset] = (unsigned char) (value & 0x00FF); 153 | data[offset+1] = (unsigned char) ((value >> 8) & 0x00FF); 154 | } 155 | 156 | 157 | /************************************************************/ 158 | /* Set24bitValue() : Place une valeur codée sur 3 octets. */ 159 | /************************************************************/ 160 | void Set24bitValue(unsigned char *data, int offset, int value) 161 | { 162 | data[offset] = (unsigned char) (value & 0x000000FF); 163 | data[offset+1] = (unsigned char) ((value >> 8) & 0x000000FF); 164 | data[offset+2] = (unsigned char) ((value >> 16) & 0x000000FF); 165 | } 166 | 167 | 168 | /************************************************************/ 169 | /* SetDWordValue() : Place une valeur codée sur 4 octets. */ 170 | /************************************************************/ 171 | void SetDWordValue(unsigned char *data, int offset, DWORD value) 172 | { 173 | data[offset] = (unsigned char) (value & 0x000000FF); 174 | data[offset+1] = (unsigned char) ((value >> 8) & 0x000000FF); 175 | data[offset+2] = (unsigned char) ((value >> 16) & 0x000000FF); 176 | data[offset+3] = (unsigned char) ((value >> 24) & 0x000000FF); 177 | } 178 | 179 | 180 | /************************************************************/ 181 | /* CreateBinaryFile() : Création d'un fichier sur disque. */ 182 | /************************************************************/ 183 | int CreateBinaryFile(char *file_path, unsigned char *data, int length) 184 | { 185 | int nb_write; 186 | FILE *fd; 187 | 188 | /* Suppression du fichier */ 189 | os_DeleteFile(file_path); 190 | 191 | /* Création du fichier */ 192 | fd = fopen(file_path,"wb"); 193 | if(fd == NULL) 194 | return(1); 195 | 196 | /* Ecriture des données */ 197 | nb_write = fwrite(data,1,length,fd); 198 | if(nb_write != length) 199 | { 200 | fclose(fd); 201 | return(2); 202 | } 203 | 204 | /* Fermeture du fichier */ 205 | fclose(fd); 206 | 207 | /* OK */ 208 | return(0); 209 | } 210 | 211 | 212 | /***************************************************************/ 213 | /* CreateTextFile() : Création d'un fichier Text sur disque. */ 214 | /***************************************************************/ 215 | int CreateTextFile(char *file_path, unsigned char *data, int length) 216 | { 217 | int nb_write; 218 | FILE *fd; 219 | 220 | /* Suppression du fichier */ 221 | os_DeleteFile(file_path); 222 | 223 | /* Création du fichier */ 224 | fd = fopen(file_path,"w"); 225 | if(fd == NULL) 226 | return(1); 227 | 228 | /* Ecriture des données */ 229 | nb_write = fwrite(data,1,length,fd); 230 | if(nb_write < length) 231 | { 232 | fclose(fd); 233 | return(2); 234 | } 235 | 236 | /* Fermeture du fichier */ 237 | fclose(fd); 238 | 239 | /* OK */ 240 | return(0); 241 | } 242 | 243 | /********************************************************/ 244 | /* BuildFileList() : Construit la liste des fichiers. */ 245 | /********************************************************/ 246 | char **BuildFileList(char *hierarchy, int *nb_file_rtn) 247 | { 248 | int i, result; 249 | char *file_path; 250 | char **tab_file; 251 | int nb_file; 252 | char hierarchy_path[2048]; 253 | char folder_path[2048]; 254 | 255 | /* Init */ 256 | *nb_file_rtn = 0; 257 | my_Memory(MEMORY_FREE_FILE,NULL,NULL); 258 | 259 | /** Y a t'il un * dans le chemin du fichier ? **/ 260 | if(strchr(hierarchy,'*') == NULL) 261 | { 262 | /* Un seul fichier */ 263 | tab_file = (char **) calloc(1,sizeof(char *)); 264 | if(tab_file == NULL) 265 | { 266 | return(NULL); 267 | } 268 | tab_file[0] = strdup(hierarchy); 269 | if(tab_file[0] == NULL) 270 | { 271 | free(tab_file); 272 | return(NULL); 273 | } 274 | *nb_file_rtn = 1; 275 | return(tab_file); 276 | } 277 | 278 | /** Hiérachie de fichier à traiter **/ 279 | strcpy(hierarchy_path,hierarchy); 280 | CleanHierarchie(hierarchy_path); 281 | 282 | /** Répertoire de départ **/ 283 | strcpy(folder_path,hierarchy_path); 284 | for(i=0; i<(int)strlen(folder_path); i++) 285 | if(folder_path[i] == '*') 286 | { 287 | folder_path[i] = '\0'; 288 | break; 289 | } 290 | for(i=strlen(folder_path); i>=0; i--) 291 | if(folder_path[i] == '\\' || folder_path[i] == '/') 292 | { 293 | folder_path[i+1] = '\0'; 294 | break; 295 | } 296 | 297 | /** Recherche des fichiers **/ 298 | result = os_GetFolderFiles(folder_path,hierarchy_path); 299 | if(result) 300 | { 301 | my_Memory(MEMORY_FREE_FILE,NULL,NULL); 302 | return(NULL); 303 | } 304 | 305 | /** Création du tableau **/ 306 | my_Memory(MEMORY_GET_FILE_NB,&nb_file,NULL); 307 | 308 | /* Allocation mémoire */ 309 | tab_file = (char **) calloc(nb_file,sizeof(char *)); 310 | if(tab_file == NULL) 311 | { 312 | my_Memory(MEMORY_FREE_FILE,NULL,NULL); 313 | return(NULL); 314 | } 315 | 316 | /* Remplissage du tableau */ 317 | for(i=1; i<=nb_file; i++) 318 | { 319 | my_Memory(MEMORY_GET_FILE,&i,&file_path); 320 | tab_file[i-1] = strdup(file_path); 321 | if(tab_file[i-1] == NULL) 322 | { 323 | my_Memory(MEMORY_FREE_FILE,NULL,NULL); 324 | return(NULL); 325 | } 326 | } 327 | 328 | /* Libération mémoire */ 329 | my_Memory(MEMORY_FREE_FILE,NULL,NULL); 330 | 331 | /* Renvoi les valeurs */ 332 | *nb_file_rtn = nb_file; 333 | return(tab_file); 334 | } 335 | 336 | 337 | /** 338 | * @brief MatchHierarchie Check if a file belongs to a directory tree 339 | * @param name File name 340 | * @param hierarchie Path 341 | * @return 342 | */ 343 | int MatchHierarchie(char *name, char *hierarchie) 344 | { 345 | int i,j,k; 346 | int length; 347 | char *hier_ptr; 348 | char *name_ptr; 349 | int result; 350 | int count; 351 | int offset; 352 | char buffer[BUFFER_SIZE]; 353 | 354 | /*** On parcours les deux chaînes ***/ 355 | for(i=0,j=0; i<(int)strlen(hierarchie); i++) 356 | { 357 | if(hierarchie[i] != '*') 358 | { 359 | if(toupper(hierarchie[i]) != toupper(name[j])) 360 | return(0); 361 | j++; 362 | } 363 | else if(hierarchie[i] == '?') 364 | j++; 365 | else 366 | { 367 | /* Si '*' dernier caractère de la chaîne => OK */ 368 | if(hierarchie[i+1] == '\0') 369 | return(1); 370 | 371 | /** S'il ne reste pas d'autre '*' : On compare la fin **/ 372 | hier_ptr = strchr(&hierarchie[i+1],'*'); 373 | if(hier_ptr == NULL) 374 | { 375 | length = strlen(&hierarchie[i+1]); 376 | if((int)strlen(&name[j]) < length) 377 | return(0); 378 | if(!mh_stricmp(&name[strlen(name)-length],&hierarchie[i+1])) 379 | return(1); 380 | else 381 | return(0); 382 | } 383 | 384 | /** On compte le nb d'occurences de la partie entre les deux '*' dans name **/ 385 | strncpy(buffer,&hierarchie[i+1],hier_ptr-&hierarchie[i+1]); 386 | buffer[hier_ptr-&hierarchie[i+1]] = '\0'; 387 | length = strlen(buffer); 388 | for(count = 0,offset = j;;count++) 389 | { 390 | name_ptr = mh_stristr(&name[offset],buffer); 391 | if(name_ptr == NULL) 392 | break; 393 | offset = (name_ptr - name) + 1; 394 | } 395 | /* Si aucune occurence => pas de matching */ 396 | if(count == 0) 397 | return(0); 398 | 399 | /** On lance la récursivité sur toutes les occurences trouvées **/ 400 | for(k=0,offset=j; kpath) 611 | free(current_filepath->path); 612 | 613 | free(current_filepath); 614 | } 615 | } 616 | 617 | 618 | /********************************************************/ 619 | /* mem_free_list() : Libération mémoire d'un tableau. */ 620 | /********************************************************/ 621 | void mem_free_list(int nb_element, char **element_tab) 622 | { 623 | int i; 624 | 625 | if(element_tab) 626 | { 627 | for(i=0; i 12 | 13 | typedef unsigned char BYTE; 14 | typedef uint16_t WORD; 15 | typedef uint32_t DWORD; 16 | 17 | #define BUFFER_SIZE 2048 18 | 19 | struct file_path 20 | { 21 | char *path; 22 | 23 | struct file_path *next; 24 | }; 25 | 26 | unsigned char *LoadTextFile(char *,int *); 27 | unsigned char *LoadBinaryFile(char *,int *); 28 | int Get24bitValue(unsigned char *,int); 29 | int GetWordValue(unsigned char *,int); 30 | int GetByteValue(unsigned char *,int); 31 | void SetWordValue(unsigned char *,int,WORD); 32 | void SetDWordValue(unsigned char *,int,DWORD); 33 | void Set24bitValue(unsigned char *,int,int); 34 | int CreateBinaryFile(char *,unsigned char *,int); 35 | int CreateTextFile(char *,unsigned char *,int); 36 | char **BuildFileList(char *,int *); 37 | int MatchHierarchie(char *,char *); 38 | void CleanHierarchie(char *); 39 | char *mh_stristr(char *,char *); 40 | int mh_stricmp(char *,char *); 41 | char **BuildUniqueListFromFile(char *,int *); 42 | int GetContainerNumber(int,int); 43 | void mem_free_list(int,char **); 44 | void mem_free_filepath(struct file_path *); 45 | 46 | /***********************************************************************/ 47 | -------------------------------------------------------------------------------- /Src/File_AppleSingle.c: -------------------------------------------------------------------------------- 1 | #include "File_AppleSingle.h" 2 | #include "log.h" 3 | #include "os/os.h" 4 | 5 | const unsigned static int AS_MAGIC = (uint32_t) 0x00051600; 6 | 7 | static uint32_t as_field32(uint32_t num) 8 | { 9 | return IS_LITTLE_ENDIAN ? swap32(num) : num; 10 | } 11 | 12 | static uint16_t as_field16(uint16_t num) 13 | { 14 | return IS_LITTLE_ENDIAN ? swap16(num) : num; 15 | } 16 | 17 | /** 18 | * Is this an AppleSingle file? 19 | * @brief ASIsAppleSingle 20 | * @param buf 21 | * @return 22 | */ 23 | bool ASIsAppleSingle(unsigned char *buf, size_t buflen) 24 | { 25 | if (buflen < sizeof(AS_MAGIC)) return false; 26 | int buf_magic; 27 | memcpy(&buf_magic, buf, sizeof(AS_MAGIC)); 28 | buf_magic = as_field32(buf_magic); 29 | 30 | return buf_magic == AS_MAGIC; 31 | } 32 | 33 | /** 34 | * Parse header out of raw data buffer. 35 | * @brief ASParseHeader 36 | * @param buf The buffer 37 | * @return 38 | */ 39 | struct as_file_header *ASParseHeader(unsigned char *buf, size_t buflen) 40 | { 41 | if (buflen < sizeof(as_file_header)) return NULL; 42 | struct as_file_header *header = malloc(sizeof(as_file_header)); 43 | if (!header) return NULL; 44 | struct as_file_header *buf_header = (as_file_header *) buf; 45 | 46 | header->magic = as_field32(buf_header->magic); 47 | header->version = as_field32(buf_header->version); 48 | header->num_entries = as_field16(buf_header->num_entries); 49 | 50 | return header; 51 | } 52 | 53 | /** 54 | * @brief ASParseProdosEntry 55 | * @param entry_buf The entry buffer 56 | * @return 57 | */ 58 | struct as_prodos_info *ASParseProdosEntry(unsigned char *entry_buf, size_t buflen) 59 | { 60 | if (buflen < sizeof(as_prodos_info)) return NULL; 61 | struct as_prodos_info *prodos_entry = malloc(sizeof(as_prodos_info)); 62 | if (!prodos_entry) return NULL; 63 | struct as_prodos_info *buf_prodos_entry = (as_prodos_info *) entry_buf; 64 | 65 | prodos_entry->access = as_field16(buf_prodos_entry->access); 66 | prodos_entry->filetype = as_field16(buf_prodos_entry->filetype); 67 | prodos_entry->auxtype = as_field32(buf_prodos_entry->auxtype); 68 | 69 | return prodos_entry; 70 | } 71 | 72 | /** 73 | * Read headers and return a list of as_file_entry pointers. 74 | * @brief ASGetEntries 75 | * @param buf 76 | * @return 77 | */ 78 | struct as_file_entry *ASGetEntries(struct as_file_header *header, unsigned char *buf, size_t buflen) 79 | { 80 | if (!header) 81 | { 82 | logf_error(" Error: Invalid AppleSingle file!\n"); 83 | return NULL; 84 | } 85 | 86 | size_t entries_length = header->num_entries * sizeof(as_file_entry); 87 | if (buflen < sizeof(as_file_header) + entries_length) return NULL; 88 | 89 | struct as_file_entry *entries = malloc(entries_length); 90 | struct as_file_entry *buf_entries = (as_file_entry *) (buf + sizeof(as_file_header)); 91 | 92 | memcpy(entries, buf_entries, header->num_entries * sizeof(as_file_entry)); 93 | 94 | if (IS_LITTLE_ENDIAN) 95 | for (int i = 0; i < header->num_entries; ++i) 96 | { 97 | entries[i].entry_id = swap32(entries[i].entry_id); 98 | entries[i].offset = swap32(entries[i].offset); 99 | entries[i].length = swap32(entries[i].length); 100 | } 101 | 102 | return entries; 103 | } 104 | 105 | /** 106 | * Grab data from data entry and place in prodos_file. 107 | * @brief ASDecorateDataFork 108 | * @param current_file The current file 109 | * @param data The data 110 | * @param data_fork_entry The data fork entry 111 | */ 112 | void ASDecorateDataFork(struct prodos_file *current_file, unsigned char *data, size_t datalen, as_file_entry *data_fork_entry) 113 | { 114 | if (data_fork_entry->entry_id != data_fork) return; 115 | if (datalen < data_fork_entry->offset + data_fork_entry->length) return; 116 | 117 | unsigned char *data_entry = malloc(data_fork_entry->length); 118 | memcpy(data_entry, data + data_fork_entry->offset, data_fork_entry->length); 119 | current_file->data = data_entry; 120 | current_file->data_length = data_fork_entry->length; 121 | 122 | return; 123 | } 124 | 125 | /** 126 | * Read ProDOS metadata struct and place in prodos_file. 127 | * 128 | * @brief ASDecorateProdosFileInfo 129 | * @param current_file The current file 130 | * @param data The data 131 | * @param prodos_entry The prodos entry 132 | */ 133 | void ASDecorateProdosFileInfo(struct prodos_file *current_file, unsigned char *data, size_t datalen, as_file_entry *prodos_entry) 134 | { 135 | if (prodos_entry->entry_id != prodos_file_info) return; 136 | if (datalen < prodos_entry->offset + prodos_entry->length) return; 137 | 138 | struct as_prodos_info *info_meta = ASParseProdosEntry( 139 | data + prodos_entry->offset, 140 | prodos_entry->length 141 | ); 142 | 143 | if (!info_meta) return; 144 | 145 | current_file->access = info_meta->access; 146 | current_file->type = info_meta->filetype; 147 | current_file->aux_type = info_meta->auxtype; 148 | 149 | return; 150 | } 151 | 152 | /** 153 | * Parse AppleSingle header and write attributes into prodos_file 154 | * struct 155 | * @brief ASDecorateProdosFile 156 | * @param current_file 157 | * @param data 158 | */ 159 | void ASDecorateProdosFile(struct prodos_file *current_file, unsigned char *data, size_t datalen) 160 | { 161 | struct as_file_header *header = ASParseHeader(data, datalen); 162 | struct as_file_entry *entries = ASGetEntries(header, data, datalen); 163 | 164 | for (int i = 0; i < header->num_entries; ++i) 165 | switch(entries[i].entry_id) 166 | { 167 | case data_fork: 168 | ASDecorateDataFork(current_file, data, datalen, &entries[i]); 169 | break; 170 | case prodos_file_info: 171 | ASDecorateProdosFileInfo(current_file, data, datalen, &entries[i]); 172 | break; 173 | default: 174 | logf_info(" Entry ID %d unsupported, ignoring!\n", entries[i].entry_id); 175 | logf_info(" (See https://tools.ietf.org/html/rfc1740 for ID lookup)\n"); 176 | break; 177 | } 178 | return; 179 | } 180 | 181 | struct as_from_prodos ASFromProdosFile(struct prodos_file *file) 182 | { 183 | struct as_file_header *as_header = malloc(sizeof(as_file_header)); 184 | as_header->magic = as_field32(AS_MAGIC); 185 | for (int i = 0; i < 4; ++i) as_header->filler[i] = 0; 186 | as_header->version = as_field32(0x00020000); 187 | as_header->num_entries = as_field16(2); 188 | 189 | uint32_t header_offset = sizeof(as_file_header) + (2 * sizeof(as_file_entry)); 190 | struct as_file_entry *data_entry = malloc(sizeof(as_file_entry)); 191 | data_entry->entry_id = as_field32(data_fork); 192 | data_entry->length = as_field32(file->data_length); 193 | data_entry->offset = as_field32(header_offset); 194 | 195 | uint32_t prodos_entry_offset = header_offset + file->data_length; 196 | struct as_file_entry *prodos_entry = malloc(sizeof(as_file_entry)); 197 | prodos_entry->entry_id = as_field32(prodos_file_info); 198 | prodos_entry->length = as_field32(sizeof(as_prodos_info)); 199 | prodos_entry->offset = as_field32(prodos_entry_offset); 200 | 201 | struct as_prodos_info *prodos_info = malloc(sizeof(as_prodos_info)); 202 | prodos_info->access = as_field16(file->entry->access); 203 | prodos_info->filetype = as_field16(file->entry->file_type); 204 | prodos_info->auxtype = as_field32(file->entry->file_aux_type); 205 | 206 | uint32_t payload_size = prodos_entry_offset + sizeof(as_prodos_info); 207 | unsigned char *payload = malloc(payload_size); 208 | unsigned char *seek = payload; 209 | 210 | memcpy(seek, as_header, sizeof(as_file_header)); 211 | seek += sizeof(as_file_header); 212 | 213 | memcpy(seek, data_entry, sizeof(as_file_entry)); 214 | seek += sizeof(as_file_entry); 215 | 216 | memcpy(seek, prodos_entry, sizeof(as_file_entry)); 217 | seek += sizeof(as_file_entry); 218 | 219 | memcpy(seek, file->data, file->data_length); 220 | seek += file->data_length; 221 | 222 | memcpy(seek, prodos_info, sizeof(as_prodos_info)); 223 | 224 | struct as_from_prodos as_file; 225 | as_file.length = payload_size; 226 | as_file.data = payload; 227 | 228 | return as_file; 229 | } 230 | -------------------------------------------------------------------------------- /Src/File_AppleSingle.h: -------------------------------------------------------------------------------- 1 | /** 2 | * AppleSingle file format support 3 | * RFC 1740: https://tools.ietf.org/html/rfc1740 4 | * 5 | * Author: David Stancu, @mach-kernel, Mar. 2018 6 | * 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "Dc_Shared.h" 18 | #include "Dc_Prodos.h" 19 | 20 | const unsigned static int AS_MAGIC; 21 | #define IS_LITTLE_ENDIAN 'APPL' == (uint32_t) 0x4150504C 22 | 23 | #pragma pack(push, 1) 24 | 25 | typedef struct as_file_header 26 | { 27 | DWORD magic; 28 | DWORD version; 29 | DWORD filler[4]; 30 | WORD num_entries; 31 | } as_file_header; 32 | 33 | typedef struct as_file_entry 34 | { 35 | DWORD entry_id; 36 | DWORD offset; 37 | DWORD length; 38 | } as_file_entry; 39 | 40 | typedef enum as_entry_types 41 | { 42 | data_fork = 1, 43 | resource_fork = 2, 44 | real_name = 3, 45 | comment = 4, 46 | icon_bw = 5, 47 | icon_color = 6, 48 | file_dates_info = 8, 49 | finder_info = 9, 50 | mac_file_info = 10, 51 | prodos_file_info = 11, 52 | msdos_file_info = 12, 53 | short_name = 13, 54 | afp_file_info = 14, 55 | directory_id = 15 56 | } as_entry_types; 57 | 58 | typedef struct as_prodos_info 59 | { 60 | WORD access; 61 | WORD filetype; 62 | DWORD auxtype; 63 | } as_prodos_info; 64 | 65 | typedef struct as_from_prodos 66 | { 67 | uint16_t length; 68 | unsigned char *data; 69 | } as_from_prodos; 70 | 71 | #pragma pack(pop) 72 | 73 | bool ASIsAppleSingle(unsigned char *buf, size_t buflen); 74 | 75 | struct as_file_header *ASParseHeader(unsigned char *buf, size_t buflen); 76 | struct as_prodos_info *ASParseProdosEntry(unsigned char *entry_buf, size_t buflen); 77 | struct as_file_entry *ASGetEntries(struct as_file_header *header, unsigned char *buf, size_t buflen); 78 | 79 | void ASDecorateDataFork(struct prodos_file *current_file, unsigned char *data, size_t datalen, as_file_entry *data_fork_entry); 80 | void ASDeocrateProdosFileInfo(struct prodos_file *current_file, unsigned char *data, size_t datalen, as_file_entry *prodos_entry); 81 | void ASDecorateProdosFile(struct prodos_file *current_file, unsigned char *data, size_t datalen); 82 | 83 | struct as_from_prodos ASFromProdosFile(struct prodos_file *file); 84 | -------------------------------------------------------------------------------- /Src/Prodos_Add.h: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /* */ 3 | /* Prodos_Add.h : Header pour la gestion des commandes ADD. */ 4 | /* */ 5 | /**********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /**********************************************************************/ 8 | 9 | int AddFile(struct prodos_image *,char *,char *,bool,int); 10 | void AddFolder(struct prodos_image *,char *,char *,bool); 11 | 12 | /***********************************************************************/ 13 | -------------------------------------------------------------------------------- /Src/Prodos_Check.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Check.c : Module pour la gestion de la commande CHECKVOLUME. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #if IS_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include "Dc_Shared.h" 18 | #include "Dc_Prodos.h" 19 | #include "Dc_Memory.h" 20 | #include "os/os.h" 21 | #include "Prodos_Check.h" 22 | #include "log.h" 23 | 24 | static char *GetObjectInfo(int,struct file_descriptive_entry *); 25 | 26 | /*****************************************************************/ 27 | /* CheckProdosImage() : Vérifie le contenu d'une image Prodos. */ 28 | /*****************************************************************/ 29 | void CheckProdosImage(struct prodos_image *current_image, int verbose) 30 | { 31 | int i, j, nb_directory, nb_file, nb_error, nb_bitmap_block, first_block_number; 32 | struct file_descriptive_entry *current_directory; 33 | struct file_descriptive_entry *current_file; 34 | struct error *current_error; 35 | char *object_info; 36 | char current_block_info[2048]; 37 | char first_block_info[2048]; 38 | char error_message[2048]; 39 | 40 | /** Blocs de Boot **/ 41 | if(verbose) 42 | { 43 | logf("; --------------------- Boot ----------------------\n"); 44 | logf("Boot;0000\n"); 45 | logf("Boot;0001\n"); 46 | } 47 | current_image->block_usage_type[0x0000] = BLOCK_TYPE_BOOT; 48 | current_image->block_usage_type[0x0001] = BLOCK_TYPE_BOOT; 49 | 50 | /** Volume directory Blocs **/ 51 | if(verbose) 52 | { 53 | logf("; --------------- Volume Directory ----------------\n"); 54 | logf("Volume;0002\n"); 55 | logf("Volume;0003\n"); 56 | logf("Volume;0004\n"); 57 | logf("Volume;0005\n"); 58 | } 59 | current_image->block_usage_type[0x0002] = BLOCK_TYPE_VOLUME; 60 | current_image->block_usage_type[0x0003] = BLOCK_TYPE_VOLUME; 61 | current_image->block_usage_type[0x0004] = BLOCK_TYPE_VOLUME; 62 | current_image->block_usage_type[0x0005] = BLOCK_TYPE_VOLUME; 63 | 64 | /** Bitmap Blocs **/ 65 | if(verbose) 66 | logf("; -------------------- Bitmap ---------------------\n"); 67 | nb_bitmap_block = GetContainerNumber(current_image->nb_block,BLOCK_SIZE*8); 68 | for(i=0; iblock_usage_type[0x0006+i] = BLOCK_TYPE_BITMAP; 73 | } 74 | 75 | /** Liste des Folders **/ 76 | if(verbose) 77 | logf("; ------------------ Folder List ------------------\n"); 78 | my_Memory(MEMORY_GET_DIRECTORY_NB,&nb_directory,NULL); 79 | for(i=1; i<=nb_directory; i++) 80 | { 81 | my_Memory(MEMORY_GET_DIRECTORY,&i,¤t_directory); 82 | if(verbose) 83 | logf("Folder;%s",current_directory->file_path); 84 | for(j=0; jnb_used_block; j++) 85 | if(current_directory->tab_used_block[j] != 0) 86 | { 87 | /* A qui ce block appartient */ 88 | if(current_image->block_usage_type[current_directory->tab_used_block[j]] != BLOCK_TYPE_EMPTY) 89 | { 90 | /* Déjà occupé ! */ 91 | object_info = GetObjectInfo(current_image->block_usage_type[current_directory->tab_used_block[j]],(struct file_descriptive_entry *)current_image->block_usage_object[current_directory->tab_used_block[j]]); 92 | sprintf(error_message,"Block %04X is claimed by Folder %s but it is already used by %s",current_directory->tab_used_block[j],current_directory->file_path,object_info); 93 | my_Memory(MEMORY_ADD_ERROR,error_message,NULL); 94 | } 95 | else 96 | { 97 | current_image->block_usage_type[current_directory->tab_used_block[j]] = BLOCK_TYPE_FOLDER; 98 | current_image->block_usage_object[current_directory->tab_used_block[j]] = current_directory; 99 | } 100 | /* Numéro du block */ 101 | if(verbose) 102 | logf(";%04X",current_directory->tab_used_block[j]); 103 | } 104 | if(verbose) 105 | logf("\n"); 106 | } 107 | 108 | /** Liste des Fichiers **/ 109 | if(verbose) 110 | logf("; ------------------- File List -------------------\n"); 111 | my_Memory(MEMORY_GET_ENTRY_NB,&nb_file,NULL); 112 | for(i=1; i<=nb_file; i++) 113 | { 114 | my_Memory(MEMORY_GET_ENTRY,&i,¤t_file); 115 | if(verbose) 116 | logf("File;%s",current_file->file_path); 117 | for(j=0; jnb_used_block; j++) 118 | if(current_file->tab_used_block[j] != 0) 119 | { 120 | /* A qui ce block appartient */ 121 | if(current_image->block_usage_type[current_file->tab_used_block[j]] != BLOCK_TYPE_EMPTY) 122 | { 123 | /* Déjà occupé ! */ 124 | object_info = GetObjectInfo(current_image->block_usage_type[current_file->tab_used_block[j]],(struct file_descriptive_entry *)current_image->block_usage_object[current_file->tab_used_block[j]]); 125 | sprintf(error_message,"Block %04X is claimed by File %s but it is already used by %s",current_file->tab_used_block[j],current_file->file_path,object_info); 126 | my_Memory(MEMORY_ADD_ERROR,error_message,NULL); 127 | } 128 | else 129 | { 130 | current_image->block_usage_type[current_file->tab_used_block[j]] = BLOCK_TYPE_FILE; 131 | current_image->block_usage_object[current_file->tab_used_block[j]] = current_file; 132 | } 133 | 134 | /* Numéro du block */ 135 | if(verbose) 136 | logf(";%04X",current_file->tab_used_block[j]); 137 | } 138 | if(verbose) 139 | logf("\n"); 140 | } 141 | 142 | /** Liste des Blocks **/ 143 | if(verbose) 144 | logf("; ------------------ Block List -------------------\n"); 145 | for(i=0, first_block_number=-1; inb_block; i++) 146 | { 147 | /** Décode le block **/ 148 | if(current_image->block_usage_type[i] == BLOCK_TYPE_BOOT) 149 | sprintf(current_block_info,"Boot"); 150 | else if(current_image->block_usage_type[i] == BLOCK_TYPE_VOLUME) 151 | sprintf(current_block_info,"Volume"); 152 | else if(current_image->block_usage_type[i] == BLOCK_TYPE_BITMAP) 153 | sprintf(current_block_info,"Bitmap"); 154 | else if(current_image->block_usage_type[i] == BLOCK_TYPE_FILE) 155 | sprintf(current_block_info,"File;%s",((struct file_descriptive_entry *)(current_image->block_usage_object[i]))->file_path); 156 | else if(current_image->block_usage_type[i] == BLOCK_TYPE_FOLDER) 157 | sprintf(current_block_info,"Folder;%s",((struct file_descriptive_entry *)(current_image->block_usage_object[i]))->file_path); 158 | else 159 | sprintf(current_block_info,"Free"); 160 | 161 | /** Vérifie ce qui est déclaré dans la Bitmap (0=occupé, 1=libre) **/ 162 | if(current_image->block_allocation_table[i] == 1 && current_image->block_usage_type[i] != 0) 163 | { 164 | sprintf(error_message,"Block %04X is declared FREE in the Bitmap, but used by %s",i,current_block_info); 165 | my_Memory(MEMORY_ADD_ERROR,error_message,NULL); 166 | } 167 | else if(current_image->block_allocation_table[i] == 0 && current_image->block_usage_type[i] == 0) 168 | { 169 | sprintf(error_message,"Block %04X is declared IN USE in the Bitmap, but it not referenced by any object",i); 170 | my_Memory(MEMORY_ADD_ERROR,error_message,NULL); 171 | } 172 | 173 | /** Teste une continuité **/ 174 | if(first_block_number == -1) 175 | { 176 | first_block_number = i; 177 | strcpy(first_block_info,current_block_info); 178 | } 179 | else 180 | { 181 | /* Fin d'une série */ 182 | if(my_stricmp(first_block_info,current_block_info)) 183 | { 184 | /* On produit la série précédente */ 185 | if(verbose) 186 | { 187 | if(i == first_block_number+1) 188 | logf("%04X ;%s\n",first_block_number,first_block_info); 189 | else 190 | logf("%04X-%04X;%s\n",first_block_number,i-1,first_block_info); 191 | } 192 | 193 | /* On stocke les infos pour la série suivante */ 194 | first_block_number = i; 195 | strcpy(first_block_info,current_block_info); 196 | } 197 | } 198 | } 199 | /* Fin */ 200 | if(verbose) 201 | { 202 | if(current_image->nb_block == first_block_number+1) 203 | logf("%04X ;%s\n",first_block_number,first_block_info); 204 | else 205 | logf("%04X-%04X;%s\n",first_block_number,current_image->nb_block-1,first_block_info); 206 | } 207 | 208 | /** Liste des erreurs **/ 209 | if(verbose) 210 | logf("; ------------------ Error List -------------------\n"); 211 | my_Memory(MEMORY_GET_ERROR_NB,&nb_error,NULL); 212 | for(i=1; i<=nb_error; i++) 213 | { 214 | my_Memory(MEMORY_GET_ERROR,&i,¤t_error); 215 | logf(" => %s\n",current_error->message); 216 | } 217 | } 218 | 219 | 220 | /************************************************************/ 221 | /* GetObjectInfo() : Crée la chaine identifiant un objet. */ 222 | /************************************************************/ 223 | static char *GetObjectInfo(int type, struct file_descriptive_entry *current_entry) 224 | { 225 | static char object_info[2048]; 226 | 227 | if(type == BLOCK_TYPE_BOOT) 228 | sprintf(object_info,"Boot"); 229 | else if(type == BLOCK_TYPE_VOLUME) 230 | sprintf(object_info,"Volume"); 231 | else if(type == BLOCK_TYPE_BITMAP) 232 | sprintf(object_info,"Bitmap"); 233 | else if(type == BLOCK_TYPE_FILE) 234 | sprintf(object_info,"File;%s",current_entry->file_path); 235 | else if(type == BLOCK_TYPE_FOLDER) 236 | sprintf(object_info,"Folder;%s",current_entry->file_path); 237 | else 238 | strcpy(object_info,"Unknown"); 239 | 240 | /* Renvoi la description */ 241 | return(&object_info[0]); 242 | } 243 | 244 | /***********************************************************************/ -------------------------------------------------------------------------------- /Src/Prodos_Check.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Check.h : Header pour la gestion de la commande CHECKVOUME. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | void CheckProdosImage(struct prodos_image *,int); 10 | 11 | /***********************************************************************/ 12 | -------------------------------------------------------------------------------- /Src/Prodos_Create.h: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /* */ 3 | /* Prodos_Create.h : Header pour la gestion des commandes CREATE. */ 4 | /* */ 5 | /**********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /**********************************************************************/ 8 | 9 | void CreateProdosFolder(struct prodos_image *,char *,bool); 10 | struct prodos_image *CreateProdosVolume(char *,char *,int,bool); 11 | struct file_descriptive_entry *CreateOneProdosFolder(struct prodos_image *,struct file_descriptive_entry *,char *,bool,int); 12 | struct file_descriptive_entry *BuildProdosFolderPath(struct prodos_image *,char *,int *,bool,int); 13 | 14 | /***********************************************************************/ -------------------------------------------------------------------------------- /Src/Prodos_Delete.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Delete.c : Module pour la gestion des commandes DELETE. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #if IS_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include "Dc_Shared.h" 18 | #include "Dc_Memory.h" 19 | #include "Dc_Prodos.h" 20 | #include "Prodos_Delete.h" 21 | #include "log.h" 22 | 23 | static void DeleteEntryFile(struct prodos_image *,struct file_descriptive_entry *); 24 | static int EmptyEntryFolder(struct prodos_image *,struct file_descriptive_entry *,int); 25 | static void DeleteEmptyFolder(struct prodos_image *,struct file_descriptive_entry *); 26 | static int compare_folder(const void *,const void *); 27 | 28 | /** 29 | * @brief Delete a ProDOS file 30 | * 31 | * @param current_image The current image 32 | * @param prodos_file_path The prodos file path 33 | */ 34 | void DeleteProdosFile(struct prodos_image *current_image, char *prodos_file_path) 35 | { 36 | int error; 37 | struct file_descriptive_entry *current_entry; 38 | 39 | // If there's a type suffix, remove it before continuing 40 | char *suffix = strchr(prodos_file_path, '#'); 41 | if (suffix && strlen(suffix + 1) == 6) *suffix = '\0'; 42 | 43 | /* Recherche l'entrée Prodos */ 44 | current_entry = GetProdosFile(current_image,prodos_file_path); 45 | if(current_entry == NULL) 46 | { 47 | logf_error(" Error : Invalid Prodos File path '%s'.\n",prodos_file_path); 48 | return; 49 | } 50 | 51 | /** Supprime une entrée Fichier **/ 52 | DeleteEntryFile(current_image,current_entry); 53 | 54 | /** Ecrit le fichier **/ 55 | error = UpdateProdosImage(current_image); 56 | } 57 | 58 | 59 | /******************************************************************/ 60 | /* DeleteEntryFile() : Suppression d'une entrée Fichier Prodos. */ 61 | /******************************************************************/ 62 | static void DeleteEntryFile(struct prodos_image *current_image, struct file_descriptive_entry *current_entry) 63 | { 64 | WORD now_date, now_time, file_count; 65 | BYTE storage_type; 66 | int i, j, block_number, nb_bitmap_block, modified, total_modified, offset, subdirectory_block, current_block; 67 | struct file_descriptive_entry *current_directory; 68 | unsigned char directory_block[BLOCK_SIZE]; 69 | unsigned char bitmap_block[BLOCK_SIZE]; 70 | 71 | /* Date actuelle */ 72 | GetCurrentDate(&now_date,&now_time); 73 | 74 | /* Nombre de block nécessaires pour stocker la table */ 75 | nb_bitmap_block = GetContainerNumber(current_image->nb_block,BLOCK_SIZE*8); 76 | 77 | /**********************************************************/ 78 | /** On va supprimer cette entrée de la structure mémoire **/ 79 | 80 | /* Supprime cette entrée de la liste des entrées */ 81 | my_Memory(MEMORY_REMOVE_ENTRY,current_entry,NULL); 82 | 83 | /** Supprime cette entrée du Répertoire dans lequel elle est enregistrée **/ 84 | current_directory = current_entry->parent_directory; 85 | if(current_directory == NULL) 86 | { 87 | /** L'entrée est à la racine du volume **/ 88 | UpdateEntryTable(UPDATE_REMOVE,¤t_image->nb_file,¤t_image->tab_file,current_entry); 89 | 90 | /** Last Modification date : Volume Header **/ 91 | GetProdosDate(now_date,¤t_image->volume_header->volume_modification_date); 92 | GetProdosTime(now_time,¤t_image->volume_header->volume_modification_time); 93 | } 94 | else 95 | { 96 | /** L'entrée est dans un sous répertoire **/ 97 | UpdateEntryTable(UPDATE_REMOVE,¤t_directory->nb_file,¤t_directory->tab_file,current_entry); 98 | 99 | /** Last Modification date : Directory **/ 100 | GetProdosDate(now_date,¤t_directory->file_modification_date); 101 | GetProdosTime(now_time,¤t_directory->file_modification_time); 102 | } 103 | 104 | /** Marque les blocs occupés par le fichier comme libres **/ 105 | for(i=0; inb_used_block; i++) 106 | { 107 | block_number = current_entry->tab_used_block[i]; 108 | current_image->block_allocation_table[block_number] = 1; /* Libre */ 109 | } 110 | current_image->nb_free_block += current_entry->nb_used_block; 111 | 112 | /***********************************/ 113 | /** On va modifier l'image disque **/ 114 | /** Directory Block **/ 115 | GetBlockData(current_image,current_entry->block_location,&directory_block[0]); 116 | 117 | /** Place les informations dans le Directory : Marque l'entrée comme supprimée **/ 118 | storage_type = 0x00; 119 | memcpy(&directory_block[current_entry->entry_offset+FILE_STORAGETYPE_OFFSET],&storage_type,sizeof(BYTE)); 120 | 121 | /* Modifie le block Directory */ 122 | SetBlockData(current_image,current_entry->block_location,&directory_block[0]); 123 | 124 | /** Sub-Directory Block **/ 125 | current_block = current_entry->block_location; 126 | subdirectory_block = GetWordValue(&directory_block[0],0); 127 | while(subdirectory_block != 0) 128 | { 129 | current_block = subdirectory_block; 130 | GetBlockData(current_image,subdirectory_block,&directory_block[0]); 131 | subdirectory_block = GetWordValue(&directory_block[0],0); 132 | } 133 | 134 | /* Une entrée en moins dans ce Directory */ 135 | file_count = GetWordValue(&directory_block[0],DIRECTORY_FILECOUNT_OFFSET); 136 | if(file_count > 0) 137 | file_count--; 138 | SetWordValue(&directory_block[0],DIRECTORY_FILECOUNT_OFFSET,file_count); 139 | 140 | /* Modifie le block Sub-Directory */ 141 | SetBlockData(current_image,current_block,&directory_block[0]); 142 | 143 | /** Marque les blocs occupés par le fichier comme libres **/ 144 | total_modified = 0; 145 | for(i=0; ivolume_header->bitmap_block+i,&bitmap_block[0]); 149 | modified = 0; 150 | 151 | /** Passe en revue les blocs utilisés du fichier **/ 152 | for(j=0; jnb_used_block; j++) 153 | if((current_entry->tab_used_block[j] >= i*8*BLOCK_SIZE) && (current_entry->tab_used_block[j] < (i+1)*8*BLOCK_SIZE)) 154 | { 155 | offset = current_entry->tab_used_block[j] - i*8*BLOCK_SIZE; 156 | bitmap_block[offset/8] |= (0x01 << (7-(offset%8))); /* 1 : Libre */ 157 | modified = 1; 158 | total_modified++; 159 | 160 | /* Pas besoin de tout parcourir */ 161 | if(total_modified == current_entry->nb_used_block) 162 | break; 163 | } 164 | 165 | /* Modifie le block Bitmap */ 166 | if(modified) 167 | SetBlockData(current_image,current_image->volume_header->bitmap_block+i,&bitmap_block[0]); 168 | 169 | /* Pas besoin de tout parcourir */ 170 | if(total_modified == current_entry->nb_used_block) 171 | break; 172 | } 173 | 174 | /* Libération mémoire de la structure */ 175 | mem_free_entry(current_entry); 176 | } 177 | 178 | 179 | /*************************************************************/ 180 | /* DeleteProdosFolder() : Suppression d'un dossier Prodos. */ 181 | /*************************************************************/ 182 | void DeleteProdosFolder(struct prodos_image *current_image, char *prodos_folder_path) 183 | { 184 | int i, j, error, nb_folder, nb_directory; 185 | struct file_descriptive_entry **tab_folder; 186 | struct file_descriptive_entry *current_entry; 187 | struct file_descriptive_entry *current_directory; 188 | 189 | /* Recherche le dossier Prodos */ 190 | current_entry = GetProdosFolder(current_image,prodos_folder_path,0); 191 | if(current_entry == NULL) 192 | { 193 | logf_error(" Error : Invalid Prodos Folder path '%s'.\n",prodos_folder_path); 194 | return; 195 | } 196 | 197 | /** Supprime tous les fichiers (récursivité dans les sous-répertoires) **/ 198 | nb_folder = EmptyEntryFolder(current_image,current_entry,1); 199 | 200 | /** Construit la liste des répertoires à supprimer **/ 201 | tab_folder = (struct file_descriptive_entry **) calloc(nb_folder,sizeof(struct file_descriptive_entry *)); 202 | if(tab_folder == NULL) 203 | { 204 | logf_error(" Error : Impossible to allocate memory for table 'tab_folder'.\n"); 205 | return; 206 | } 207 | my_Memory(MEMORY_GET_DIRECTORY_NB,&nb_directory,NULL); 208 | for(i=1, j=0; i<=nb_directory; i++) 209 | { 210 | my_Memory(MEMORY_GET_DIRECTORY,&i,¤t_directory); 211 | if(current_directory->delete_folder_depth > 0) 212 | tab_folder[j++] = current_directory; 213 | } 214 | qsort(tab_folder,nb_folder,sizeof(struct file_descriptive_entry *),compare_folder); 215 | 216 | /** Supprime tous les Sous-répertoires vides, par ordre de niveau **/ 217 | for(i=0; idelete_folder_depth = depth; 238 | 239 | /** Supprime tous les fichiers du réperoire **/ 240 | while(current_entry->nb_file > 0) 241 | DeleteEntryFile(current_image,current_entry->tab_file[0]); 242 | 243 | /** Vide tous les sous-répertoires de leurs fichiers (récursivité) **/ 244 | for(i=0; inb_directory; i++) 245 | nb_folder += EmptyEntryFolder(current_image,current_entry->tab_directory[i],depth+1); 246 | 247 | /* Renvoi le nombre de sous-dossier */ 248 | return(nb_folder); 249 | } 250 | 251 | 252 | /**********************************************************/ 253 | /* DeleteEmptyFolder() : Suppression d'un dossier vide. */ 254 | /**********************************************************/ 255 | void DeleteEmptyFolder(struct prodos_image *current_image, struct file_descriptive_entry *current_entry) 256 | { 257 | WORD now_date, now_time, file_count; 258 | int i, j, block_number, modified, offset, current_block, subdirectory_block, nb_bitmap_block, total_modified; 259 | BYTE storage_type; 260 | struct file_descriptive_entry *current_directory; 261 | unsigned char directory_block[BLOCK_SIZE]; 262 | unsigned char bitmap_block[BLOCK_SIZE]; 263 | 264 | /* On vérifie que le répertoire est vide */ 265 | if(current_entry->nb_file != 0 || current_entry->nb_directory != 0) 266 | return; 267 | 268 | /* Date actuelle */ 269 | GetCurrentDate(&now_date,&now_time); 270 | 271 | /* Nombre de block nécessaires pour stocker la table */ 272 | nb_bitmap_block = GetContainerNumber(current_image->nb_block,BLOCK_SIZE*8); 273 | 274 | /**********************************************************/ 275 | /** On va supprimer cette entrée de la structure mémoire **/ 276 | 277 | /* Supprime cette entrée de la liste des entrées */ 278 | my_Memory(MEMORY_REMOVE_DIRECTORY,current_entry,NULL); 279 | 280 | /** Supprime cette entrée répertoire du Répertoire dans lequel elle est enregistrée **/ 281 | current_directory = current_entry->parent_directory; 282 | if(current_directory == NULL) 283 | { 284 | /** Le répertoire est à la racine du volume **/ 285 | UpdateEntryTable(UPDATE_REMOVE,¤t_image->nb_directory,¤t_image->tab_directory,current_entry); 286 | 287 | /** Last Modification date : Volume Header **/ 288 | GetProdosDate(now_date,¤t_image->volume_header->volume_modification_date); 289 | GetProdosTime(now_time,¤t_image->volume_header->volume_modification_time); 290 | } 291 | else 292 | { 293 | /** Le répertoire est dans un sous répertoire **/ 294 | UpdateEntryTable(UPDATE_REMOVE,¤t_directory->nb_directory,¤t_directory->tab_directory,current_entry); 295 | 296 | /** Last Modification date : Directory **/ 297 | GetProdosDate(now_date,¤t_directory->file_modification_date); 298 | GetProdosTime(now_time,¤t_directory->file_modification_time); 299 | } 300 | 301 | /** Marque les blocs occupés par le répertoire comme libres **/ 302 | for(i=0; inb_used_block; i++) 303 | { 304 | block_number = current_entry->tab_used_block[i]; 305 | current_image->block_allocation_table[block_number] = 1; /* Libre */ 306 | } 307 | current_image->nb_free_block += current_entry->nb_used_block; 308 | 309 | /***********************************/ 310 | /** On va modifier l'image disque **/ 311 | /** Directory Block **/ 312 | GetBlockData(current_image,current_entry->block_location,&directory_block[0]); 313 | 314 | /** Place les informations dans le Directory : Marque l'entrée comme supprimée **/ 315 | storage_type = 0x00; 316 | memcpy(&directory_block[current_entry->entry_offset+FILE_STORAGETYPE_OFFSET],&storage_type,sizeof(BYTE)); 317 | 318 | /* Modifie le block Directory */ 319 | SetBlockData(current_image,current_entry->block_location,&directory_block[0]); 320 | 321 | /** Sub-Directory Block **/ 322 | current_block = current_entry->block_location; 323 | subdirectory_block = GetWordValue(&directory_block[0],0); 324 | while(subdirectory_block != 0) 325 | { 326 | current_block = subdirectory_block; 327 | GetBlockData(current_image,subdirectory_block,&directory_block[0]); 328 | subdirectory_block = GetWordValue(&directory_block[0],0); 329 | } 330 | 331 | /* Une entrée en moins dans ce Directory */ 332 | file_count = GetWordValue(&directory_block[0],DIRECTORY_FILECOUNT_OFFSET); 333 | if(file_count > 0) 334 | file_count--; 335 | SetWordValue(&directory_block[0],DIRECTORY_FILECOUNT_OFFSET,file_count); 336 | 337 | /* Modifie le block Sub-Directory */ 338 | SetBlockData(current_image,current_block,&directory_block[0]); 339 | 340 | /** Marque les blocs occupés par le répertoire comme libres **/ 341 | total_modified = 0; 342 | for(i=0; ivolume_header->bitmap_block+i,&bitmap_block[0]); 346 | modified = 0; 347 | 348 | /** Passe en revue les blocs utilisés du répertoire **/ 349 | for(j=0; jnb_used_block; j++) 350 | if((current_entry->tab_used_block[j] >= i*8*BLOCK_SIZE) && (current_entry->tab_used_block[j] < (i+1)*8*BLOCK_SIZE)) 351 | { 352 | offset = current_entry->tab_used_block[j] - i*8*BLOCK_SIZE; 353 | bitmap_block[offset/8] |= (0x01 << (7-(offset%8))); /* 1 : Libre */ 354 | modified = 1; 355 | total_modified++; 356 | 357 | /* Pas besoin de tout parcourir */ 358 | if(total_modified == current_entry->nb_used_block) 359 | break; 360 | } 361 | 362 | /* Modifie le block Bitmap */ 363 | if(modified) 364 | SetBlockData(current_image,current_image->volume_header->bitmap_block+i,&bitmap_block[0]); 365 | 366 | /* Pas besoin de tout parcourir */ 367 | if(total_modified == current_entry->nb_used_block) 368 | break; 369 | } 370 | 371 | /* Libération mémoire de la structure */ 372 | mem_free_entry(current_entry); 373 | } 374 | 375 | 376 | /**********************************************************/ 377 | /* DeleteProdosVolume() : Suppression du volume Prodos. */ 378 | /**********************************************************/ 379 | void DeleteProdosVolume(struct prodos_image *current_image) 380 | { 381 | WORD now_date, now_time, nb_entry; 382 | int i, j, error, nb_bitmap_block, block_number; 383 | unsigned char volume_block[BLOCK_SIZE]; 384 | unsigned char bitmap_block[BLOCK_SIZE]; 385 | unsigned char data_block[BLOCK_SIZE]; 386 | 387 | /* Date actuelle */ 388 | GetCurrentDate(&now_date,&now_time); 389 | 390 | /* Nombre de block nécessaires pour stocker la table */ 391 | nb_bitmap_block = GetContainerNumber(current_image->nb_block,BLOCK_SIZE*8); 392 | 393 | /************************************************************/ 394 | /** On va supprimer tous les fichiers la structure mémoire **/ 395 | /* Plus de fichier à la racine */ 396 | current_image->nb_file = 0; 397 | if(current_image->tab_file) 398 | free(current_image->tab_file); 399 | current_image->tab_file = NULL; 400 | /* Plus de fichier à la racine */ 401 | current_image->nb_directory = 0; 402 | if(current_image->tab_directory) 403 | free(current_image->tab_directory); 404 | current_image->tab_directory = NULL; 405 | 406 | /* Libération mémoire : Plus de fichiers nul part */ 407 | my_Memory(MEMORY_FREE_DIRECTORY,NULL,NULL); 408 | my_Memory(MEMORY_FREE_ENTRY,NULL,NULL); 409 | 410 | /* Nettoyage de la Bitmap */ 411 | for(i=0; inb_block; i++) 412 | current_image->block_allocation_table[i] = (i < (2+4+nb_bitmap_block)) ? 0 : 1; /* 0 : Busy / 1 : Free */ 413 | current_image->nb_free_block = current_image->nb_block - (2 + 4 + nb_bitmap_block); 414 | 415 | /** Volume Header **/ 416 | GetProdosDate(now_date,¤t_image->volume_header->volume_modification_date); 417 | GetProdosTime(now_time,¤t_image->volume_header->volume_modification_time); 418 | 419 | /***********************************/ 420 | /** On va modifier l'image disque **/ 421 | 422 | /** Nettoyage du Volume Directory **/ 423 | for(i=0; i<4; i++) 424 | { 425 | /* Volume Block */ 426 | GetBlockData(current_image,2+i,&volume_block[0]); 427 | 428 | /** Place les informations dans le Volume Header **/ 429 | if(i == 0) 430 | { 431 | /** Volume Header **/ 432 | /* Nombre d'entrées */ 433 | nb_entry = 0; 434 | memcpy(&volume_block[VOLUME_FILECOUNT_OFFSET],&nb_entry,sizeof(WORD)); 435 | 436 | /* Date de Modif */ 437 | memcpy(&volume_block[VOLUME_DATEMODIF_OFFSET],&now_date,sizeof(WORD)); 438 | memcpy(&volume_block[VOLUME_TIMEMODIF_OFFSET],&now_time,sizeof(WORD)); 439 | 440 | /** Entrées Suivantes **/ 441 | memset(&volume_block[4+0x27],0,BLOCK_SIZE-(4+0x27)); 442 | } 443 | else 444 | memset(&volume_block[4],0,BLOCK_SIZE-4); 445 | 446 | /* Modifie le block */ 447 | SetBlockData(current_image,2+i,&volume_block[0]); 448 | } 449 | 450 | /** Nettoyage de la Bitmap **/ 451 | /* On vide tous les block */ 452 | block_number = current_image->volume_header->bitmap_block; 453 | for(i=0; inb_block) 464 | if(current_image->block_allocation_table[(i*8*BLOCK_SIZE)+j] == 1) 465 | bitmap_block[j/8] |= (0x01 << (7-(j%8))); 466 | 467 | 468 | /* Modifie le block */ 469 | SetBlockData(current_image,block_number+i,&bitmap_block[0]); 470 | } 471 | 472 | /** Vide tous les blocs **/ 473 | memset(&data_block[0],0,BLOCK_SIZE); 474 | for(i=(block_number+nb_bitmap_block); inb_block; i++) 475 | SetBlockData(current_image,i,&data_block[0]); 476 | 477 | /** Ecrit le fichier **/ 478 | error = UpdateProdosImage(current_image); 479 | } 480 | 481 | 482 | /*******************************************************************/ 483 | /* compare_folder() : Fonction de comparaison pour le Quick Sort. */ 484 | /*******************************************************************/ 485 | static int compare_folder(const void *data_1, const void *data_2) 486 | { 487 | struct file_descriptive_entry *entry_1; 488 | struct file_descriptive_entry *entry_2; 489 | 490 | /* Récupération des paramètres */ 491 | entry_1 = *((struct file_descriptive_entry **) data_1); 492 | entry_2 = *((struct file_descriptive_entry **) data_2); 493 | 494 | /* Comparaison des noms */ 495 | if(entry_1->delete_folder_depth == entry_2->delete_folder_depth) 496 | return(0); 497 | else if(entry_1->delete_folder_depth < entry_2->delete_folder_depth) 498 | return(1); 499 | else 500 | return(-1); 501 | } 502 | 503 | /***********************************************************************/ 504 | -------------------------------------------------------------------------------- /Src/Prodos_Delete.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Delete.h : Header pour la gestion des commandes DELETE. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /***********************************************************************/ 8 | 9 | void DeleteProdosFile(struct prodos_image *,char *); 10 | void DeleteProdosFolder(struct prodos_image *,char *); 11 | void DeleteProdosVolume(struct prodos_image *); 12 | 13 | /***********************************************************************/ -------------------------------------------------------------------------------- /Src/Prodos_Dump.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Dump.c : Module pour la gestion de la commande CATALOG. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #if IS_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include "Dc_Shared.h" 18 | #include "Dc_Prodos.h" 19 | #include "Dc_Memory.h" 20 | #include "os/os.h" 21 | #include "Prodos_Dump.h" 22 | #include "log.h" 23 | 24 | static void DumpVolumeFooter(struct prodos_image *,int); 25 | static void DumpOneDirectory(struct file_descriptive_entry *,int); 26 | static void DumpOneFile(struct file_descriptive_entry *,int); 27 | static void DumpDirectoryEntries(struct prodos_image *,struct file_descriptive_entry *); 28 | static void DumpOneEntry(struct prodos_image *,struct file_descriptive_entry *); 29 | 30 | /*************************************************************/ 31 | /* DumpProdosImage() : Dump le contenu d'une image Prodos. */ 32 | /*************************************************************/ 33 | void DumpProdosImage(struct prodos_image *current_image, int dump_structure) 34 | { 35 | int i, max_depth, nb_directory; 36 | struct file_descriptive_entry *current_directory; 37 | 38 | /** Calcule la profondeur max **/ 39 | max_depth = 1; 40 | my_Memory(MEMORY_GET_DIRECTORY_NB,&nb_directory,NULL); 41 | for(i=1; i<=nb_directory; i++) 42 | { 43 | my_Memory(MEMORY_GET_DIRECTORY,&i,¤t_directory); 44 | if(current_directory->depth > max_depth) 45 | max_depth = current_directory->depth; 46 | } 47 | 48 | /* Ligne de Label */ 49 | logf(" Name"); 50 | for(i=strlen(" Name"); ivolume_header->volume_name_case); 56 | 57 | /** Volume : Files **/ 58 | for(i=0; inb_file; i++) 59 | DumpOneFile(current_image->tab_file[i],max_depth); 60 | 61 | /** Volume : Sub Directory **/ 62 | for(i=0; inb_directory; i++) 63 | DumpOneDirectory(current_image->tab_directory[i],max_depth); 64 | 65 | /** Volume Footer **/ 66 | DumpVolumeFooter(current_image,max_depth); 67 | 68 | /*** On Dump la structure interne des entrées ***/ 69 | if(dump_structure == 1) 70 | { 71 | /* Début */ 72 | logf("----------------------------------------------------------------------\n"); 73 | 74 | /** All Entries **/ 75 | for(i=0; inb_file; i++) 76 | DumpOneEntry(current_image,current_image->tab_file[i]); 77 | 78 | /** All Directory (recursivity) **/ 79 | for(i=0; inb_directory; i++) 80 | DumpDirectoryEntries(current_image,current_image->tab_directory[i]); 81 | } 82 | } 83 | 84 | 85 | /***********************************************************************/ 86 | /* DumpOneDirectory() : Dump les infomations d'une entrée Directory. */ 87 | /***********************************************************************/ 88 | static void DumpOneDirectory(struct file_descriptive_entry *current_file, int max_depth) 89 | { 90 | int i; 91 | 92 | /* Profondeur */ 93 | for(i=0; idepth; i++) 94 | logf(" "); 95 | 96 | /* Directory Name */ 97 | logf("/%s/\n",current_file->file_name_case); 98 | 99 | /** All File **/ 100 | for(i=0; inb_file; i++) 101 | DumpOneFile(current_file->tab_file[i],max_depth); 102 | 103 | /** All Directory (recursivity) **/ 104 | for(i=0; inb_directory; i++) 105 | DumpOneDirectory(current_file->tab_directory[i],max_depth); 106 | } 107 | 108 | 109 | /*************************************************************/ 110 | /* DumpOneFile() : Dump les infomations d'une entrée File. */ 111 | /*************************************************************/ 112 | static void DumpOneFile(struct file_descriptive_entry *current_file, int max_depth) 113 | { 114 | int i; 115 | char buffer[8192]; 116 | 117 | /* Init */ 118 | buffer[0] = '\0'; 119 | 120 | /* Profondeur */ 121 | for(i=0; idepth; i++) 122 | strcat(buffer," "); 123 | 124 | /* Nom Fichier */ 125 | strcat(buffer,current_file->file_name_case); 126 | 127 | /* Max Depth */ 128 | for(i=2*current_file->depth+strlen(current_file->file_name_case); ifile_type_ascii,!my_stricmp(current_file->storage_type_ascii_short,"Fork")?"+":" "); 134 | /* Aux Type */ 135 | sprintf(&buffer[strlen(buffer)],"$%04X ",current_file->file_aux_type); 136 | /* Taille Data+Resource */ 137 | sprintf(&buffer[strlen(buffer)],"%7d ",current_file->data_size+current_file->resource_size); 138 | /* Taille Data */ 139 | sprintf(&buffer[strlen(buffer)],"%7d ",current_file->data_size); 140 | /* Taille Resource */ 141 | sprintf(&buffer[strlen(buffer)],"%6d ",current_file->resource_size); 142 | /* Block Data */ 143 | sprintf(&buffer[strlen(buffer)],"%4d ",current_file->data_block); 144 | /* Block Resource */ 145 | sprintf(&buffer[strlen(buffer)],"%4d ",current_file->resource_block); 146 | /* Sparse Block */ 147 | sprintf(&buffer[strlen(buffer)],"%4d ",current_file->nb_sparse); 148 | /* Index Block */ 149 | sprintf(&buffer[strlen(buffer)],"%4d ",current_file->index_block); 150 | /* Encodage */ 151 | sprintf(&buffer[strlen(buffer)],"%s ",current_file->storage_type_ascii_short); 152 | /* Access */ 153 | sprintf(&buffer[strlen(buffer)],"%4s ",current_file->access_ascii); 154 | /* Creation Date */ 155 | sprintf(&buffer[strlen(buffer)],"%s %s ",current_file->file_creation_date.ascii,current_file->file_creation_time.ascii); 156 | /* Modification Date */ 157 | sprintf(&buffer[strlen(buffer)],"%s %s ",current_file->file_modification_date.ascii,current_file->file_modification_time.ascii); 158 | 159 | logf("%s\n",buffer); 160 | } 161 | 162 | 163 | /*************************************************************/ 164 | /* DumpVolumeFooter() : Dump les informations d'un Volume. */ 165 | /*************************************************************/ 166 | static void DumpVolumeFooter(struct prodos_image *current_image, int max_depth) 167 | { 168 | int i, nb_directory, nb_file; 169 | char buffer[8192] = ""; 170 | 171 | my_Memory(MEMORY_GET_DIRECTORY_NB,&nb_directory,NULL); 172 | my_Memory(MEMORY_GET_ENTRY_NB,&nb_file,NULL); 173 | 174 | /* Max Depth */ 175 | for(i=0; inb_block); 180 | sprintf(&buffer[strlen(buffer)],"Free : %d ",current_image->nb_free_block); 181 | sprintf(&buffer[strlen(buffer)],"File : %d ",nb_file); 182 | sprintf(&buffer[strlen(buffer)],"Directory : %d",nb_directory); 183 | 184 | /* Affichage */ 185 | logf("%s\n",buffer); 186 | } 187 | 188 | 189 | /********************************************************************************/ 190 | /* DumpDirectoryEntries() : Dump les infomations des entrées d'un répertoire. */ 191 | /********************************************************************************/ 192 | static void DumpDirectoryEntries(struct prodos_image *current_image, struct file_descriptive_entry *current_file) 193 | { 194 | int i; 195 | 196 | /* Current SubDirectory entry */ 197 | DumpOneEntry(current_image,current_file); 198 | 199 | /** All Entries **/ 200 | for(i=0; inb_file; i++) 201 | DumpOneEntry(current_image,current_file->tab_file[i]); 202 | 203 | /** All Directory (recursivity) **/ 204 | for(i=0; inb_directory; i++) 205 | DumpDirectoryEntries(current_image,current_file->tab_directory[i]); 206 | } 207 | 208 | 209 | /*********************************************************/ 210 | /* DumpOneEntry() : Dump les infomations d'une entrée. */ 211 | /*********************************************************/ 212 | static void DumpOneEntry(struct prodos_image *current_image, struct file_descriptive_entry *current_file) 213 | { 214 | int i; 215 | char prefix[1024]; 216 | 217 | /** On utilise la profondeur du fichier comme décalage **/ 218 | for(i=0; i<2*current_file->depth; i++) 219 | prefix[i] = ' '; 220 | prefix[2*current_file->depth] = '\0'; 221 | 222 | /* Path */ 223 | logf("%sFile Path : %s\n",prefix,current_file->file_path); 224 | logf("%sName Length : %d\n",prefix,current_file->name_length); 225 | logf("%sFile Name : %s\n",prefix,current_file->file_name); 226 | logf("%sFile Name Case : %s\n",prefix,current_file->file_name_case); 227 | logf("%sLower Case : %04X\n",prefix,current_file->lowercase); 228 | logf("-----\n"); 229 | 230 | /* Storage Type */ 231 | logf("%sStorage Type : %02X\n",prefix,current_file->storage_type); 232 | logf("%sStorage Type Ascii : %s\n",prefix,current_file->storage_type_ascii); 233 | logf("%sStorage Type Ascii Short : %s\n",prefix,current_file->storage_type_ascii_short); 234 | logf("-----\n"); 235 | 236 | /* File Type */ 237 | logf("%sFile Type : %02X\n",prefix,current_file->file_type); 238 | logf("%sFile Aux Type : %04X\n",prefix,current_file->file_aux_type); 239 | logf("%sFile Type Ascii : %s\n",prefix,current_file->file_type_ascii); 240 | logf("-----\n"); 241 | 242 | /* Création / Modification Date + Version */ 243 | logf("%sFile Creation Date : %s\n",prefix,current_file->file_creation_date.ascii); 244 | logf("%sFile Creation Time : %s\n",prefix,current_file->file_creation_time.ascii); 245 | logf("%sFile Modification Date : %s\n",prefix,current_file->file_modification_date.ascii); 246 | logf("%sFile Modification Time : %s\n",prefix,current_file->file_modification_time.ascii); 247 | logf("%sVersion Created : %d\n",prefix,current_file->version_created); 248 | logf("%sMin Version : %d\n",prefix,current_file->min_version); 249 | logf("-----\n"); 250 | 251 | /* Access */ 252 | logf("%sAccess : %02X\n",prefix,current_file->access); 253 | logf("%sAccess Ascii : %s\n",prefix,current_file->access_ascii); 254 | logf("-----\n"); 255 | 256 | /* Size */ 257 | logf("%sBlocks Used : %d\n",prefix,current_file->blocks_used); 258 | logf("%sEOF Location : %d\n",prefix,current_file->eof_location); 259 | logf("%sData Block : %d\n",prefix,current_file->data_block); 260 | logf("%sData Size : %d\n",prefix,current_file->data_size); 261 | logf("%sResource Block : %d\n",prefix,current_file->resource_block); 262 | logf("%sResource Size : %d\n",prefix,current_file->resource_size); 263 | logf("%sSparse Block : %d\n",prefix,current_file->nb_sparse); 264 | logf("%sIndex Block : %d\n",prefix,current_file->index_block); 265 | logf("-----\n"); 266 | 267 | /* Divers */ 268 | logf("%sDepth : %d\n",prefix,current_file->depth); 269 | logf("%sStruct Size : %d\n",prefix,current_file->struct_size); 270 | logf("%sEntry Offset [this block] : %d\n",prefix,current_file->entry_offset); 271 | logf("%sKey Pointer Block [Data] : %d %04X\n",prefix,current_file->key_pointer_block,current_file->key_pointer_block); 272 | logf("%sHeader Pointer Block [Dir] : %d %04X\n",prefix,current_file->header_pointer_block,current_file->header_pointer_block); 273 | logf("%sBlock Location [Entry] : %d %04X\n",prefix,current_file->block_location,current_file->block_location); 274 | if(current_file->parent_directory == NULL) 275 | logf("%sParent Directory : /%s\n",prefix,current_image->volume_header->volume_name_case); 276 | else 277 | logf("%sParent Directory : %s\n",prefix,current_file->parent_directory->file_path); 278 | 279 | /* Fin */ 280 | logf("----------------------------------------------------------------------\n"); 281 | } 282 | 283 | /*************************************************************************/ 284 | -------------------------------------------------------------------------------- /Src/Prodos_Dump.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Dump.h : Header pour la gestion de la commande CATALOG. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | void DumpProdosImage(struct prodos_image *,int); 10 | 11 | /***********************************************************************/ 12 | -------------------------------------------------------------------------------- /Src/Prodos_Extract.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Extract.c : Module pour la gestion des commandes EXTRACT. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef IS_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include "Dc_Shared.h" 18 | #include "Dc_Prodos.h" 19 | #include "os/os.h" 20 | #include "Prodos_Extract.h" 21 | #include "File_AppleSingle.h" 22 | #include "log.h" 23 | 24 | static int CreateOutputFile(struct prodos_file *,char *, bool); 25 | static void SetFileInformation(char *,struct prodos_file *); 26 | 27 | /** 28 | * Extracts one file 29 | * 30 | * @brief ExtractOneFile 31 | * 32 | * @param current_image 33 | * @param prodos_file_path 34 | * @param output_directory_path 35 | * @param output_apple_single 36 | */ 37 | void ExtractOneFile(struct prodos_image *current_image, char *prodos_file_path, char *output_directory_path, bool output_apple_single) 38 | { 39 | int error; 40 | struct file_descriptive_entry *current_entry; 41 | struct prodos_file *current_file; 42 | 43 | /** Recherche l'entrée du fichier **/ 44 | current_entry = GetProdosFile(current_image,prodos_file_path); 45 | if(current_entry == NULL) 46 | return; 47 | 48 | /** Allocation mémoire **/ 49 | current_file = (struct prodos_file *) calloc(1,sizeof(struct prodos_file)); 50 | if(current_file == NULL) 51 | { 52 | logf_error(" Error : Can't get file from Image : Memory Allocation impossible.\n"); 53 | return; 54 | } 55 | current_file->entry = current_entry; 56 | 57 | /** Récupère les data de ce fichier **/ 58 | error = GetDataFile(current_image,current_entry,current_file); 59 | if(error) 60 | { 61 | logf_error(" Error : Can't get file from Image : Memory Allocation impossible.\n"); 62 | mem_free_file(current_file); 63 | return; 64 | } 65 | 66 | /** Création du fichier sur disque **/ 67 | error = CreateOutputFile(current_file,output_directory_path,output_apple_single); 68 | 69 | /* Libération mémoire */ 70 | mem_free_file(current_file); 71 | } 72 | 73 | 74 | /************************************************************************/ 75 | /* ExtractFolderFiles() : Fonction récursive d'extraction de fichier. */ 76 | /************************************************************************/ 77 | void ExtractFolderFiles(struct prodos_image *current_image, struct file_descriptive_entry *folder_entry, char *output_directory_path, bool output_apple_single) 78 | { 79 | int i, error; 80 | char *windows_folder_path; 81 | struct file_descriptive_entry *current_entry; 82 | struct prodos_file *current_file; 83 | 84 | /** Création du dossier sur disque **/ 85 | /* Chemin du dossier */ 86 | windows_folder_path = (char *) calloc(strlen(output_directory_path) + strlen(folder_entry->file_name_case) + 256,sizeof(char)); 87 | if(windows_folder_path == NULL) 88 | { 89 | logf_error(" Error : Can't extract folder files from Image : Memory Allocation impossible.\n"); 90 | current_image->nb_extract_error++; 91 | return; 92 | } 93 | strcpy(windows_folder_path,output_directory_path); 94 | if(strlen(windows_folder_path) > 0) 95 | if(windows_folder_path[strlen(windows_folder_path)-1] != '\\' && windows_folder_path[strlen(windows_folder_path)-1] != '/') 96 | strcat(windows_folder_path,FOLDER_CHARACTER); 97 | strcat(windows_folder_path,folder_entry->file_name_case); 98 | strcat(windows_folder_path,FOLDER_CHARACTER); 99 | 100 | /* Création du dossier */ 101 | error = os_CreateDirectory(windows_folder_path); 102 | if(error) 103 | { 104 | logf_error(" Error : Can't create folder : '%s'.\n",windows_folder_path); 105 | current_image->nb_extract_error++; 106 | free(windows_folder_path); 107 | return; 108 | } 109 | current_image->nb_extract_folder++; 110 | 111 | /*****************************************************/ 112 | /** Traitement de tous les fichiers du répertoire **/ 113 | for(i=0; inb_file; i++) 114 | { 115 | /* Entrée du fichier */ 116 | current_entry = folder_entry->tab_file[i]; 117 | 118 | /* Information */ 119 | logf_info(" o Extract File : %s\n",current_entry->file_path); 120 | 121 | /** Allocation mémoire **/ 122 | current_file = (struct prodos_file *) calloc(1,sizeof(struct prodos_file)); 123 | if(current_file == NULL) 124 | { 125 | logf_error(" Error : Can't get file from Image : Memory Allocation impossible.\n"); 126 | current_image->nb_extract_error++; 127 | continue; 128 | } 129 | current_file->entry = current_entry; 130 | 131 | /** Récupère les data de ce fichier **/ 132 | error = GetDataFile(current_image,current_entry,current_file); 133 | if(error) 134 | { 135 | logf_error(" Error : Can't get file from Image : Memory Allocation impossible.\n"); 136 | current_image->nb_extract_error++; 137 | mem_free_file(current_file); 138 | continue; 139 | } 140 | 141 | /** Création du fichier sur disque **/ 142 | error = CreateOutputFile(current_file,windows_folder_path,output_apple_single); 143 | 144 | /* Libération mémoire */ 145 | mem_free_file(current_file); 146 | 147 | /* Stat */ 148 | if(error) 149 | current_image->nb_extract_error++; 150 | else 151 | current_image->nb_extract_file++; 152 | } 153 | 154 | /*****************************************************/ 155 | /** Traitement de tous les dossiers du répertoire **/ 156 | for(i=0; inb_directory; i++) 157 | { 158 | /* Entrée du fichier */ 159 | current_entry = folder_entry->tab_directory[i]; 160 | 161 | /* Information */ 162 | logf_info(" + Extract Folder : %s\n",current_entry->file_path); 163 | 164 | /** Récursivité **/ 165 | ExtractFolderFiles(current_image,current_entry,windows_folder_path,output_apple_single); 166 | } 167 | 168 | /** Libération mémoire **/ 169 | free(windows_folder_path); 170 | } 171 | 172 | 173 | /****************************************************************************/ 174 | /* ExtractVolumeFiles() : Fonction d'extraction des fichiers d'un volume. */ 175 | /****************************************************************************/ 176 | void ExtractVolumeFiles(struct prodos_image *current_image, char *output_directory_path, bool output_apple_single) 177 | { 178 | int i, error; 179 | char *windows_folder_path; 180 | struct file_descriptive_entry *current_entry; 181 | struct prodos_file *current_file; 182 | 183 | /** Création du dossier sur disque **/ 184 | /* Chemin du dossier */ 185 | windows_folder_path = (char *) calloc(strlen(output_directory_path) + strlen(current_image->volume_header->volume_name_case) + 256,sizeof(char)); 186 | if(windows_folder_path == NULL) 187 | { 188 | logf_error(" Error : Can't extract files from Image : Memory Allocation impossible.\n"); 189 | current_image->nb_extract_error++; 190 | return; 191 | } 192 | strcpy(windows_folder_path,output_directory_path); 193 | if(strlen(windows_folder_path) > 0) 194 | if(windows_folder_path[strlen(windows_folder_path)-1] != '\\' && windows_folder_path[strlen(windows_folder_path)-1] != '/') 195 | strcat(windows_folder_path,FOLDER_CHARACTER); 196 | strcat(windows_folder_path,current_image->volume_header->volume_name_case); 197 | strcat(windows_folder_path,FOLDER_CHARACTER); 198 | 199 | /* Création du dossier */ 200 | error = os_CreateDirectory(windows_folder_path); 201 | if(error) 202 | { 203 | logf_error(" Error : Can't create folder : '%s'.\n",windows_folder_path); 204 | free(windows_folder_path); 205 | current_image->nb_extract_error++; 206 | return; 207 | } 208 | 209 | /****************************************************/ 210 | /** Traitement de tous les fichiers de la racine **/ 211 | for(i=0; inb_file; i++) 212 | { 213 | /* Entrée du fichier */ 214 | current_entry = current_image->tab_file[i]; 215 | 216 | /* Information */ 217 | logf_info(" o Extract File : %s\n",current_entry->file_path); 218 | 219 | /** Allocation mémoire **/ 220 | current_file = (struct prodos_file *) calloc(1,sizeof(struct prodos_file)); 221 | if(current_file == NULL) 222 | { 223 | logf_error(" Error : Can't get file from Image : Memory Allocation impossible.\n"); 224 | current_image->nb_extract_error++; 225 | continue; 226 | } 227 | current_file->entry = current_entry; 228 | 229 | /** Récupère les data de ce fichier **/ 230 | error = GetDataFile(current_image,current_entry,current_file); 231 | if(error) 232 | { 233 | logf_error(" Error : Can't get file from Image : Memory Allocation impossible.\n"); 234 | current_image->nb_extract_error++; 235 | mem_free_file(current_file); 236 | continue; 237 | } 238 | 239 | /** Création du fichier sur disque **/ 240 | error = CreateOutputFile(current_file,windows_folder_path, output_apple_single); 241 | 242 | /* Libération mémoire */ 243 | mem_free_file(current_file); 244 | 245 | /* Stat */ 246 | if(error) 247 | current_image->nb_extract_error++; 248 | else 249 | current_image->nb_extract_file++; 250 | } 251 | 252 | /****************************************************/ 253 | /** Traitement de tous les dossiers de la racine **/ 254 | for(i=0; inb_directory; i++) 255 | { 256 | /* Entrée du fichier */ 257 | current_entry = current_image->tab_directory[i]; 258 | 259 | /* Information */ 260 | logf_info(" + Extract Folder : %s\n",current_entry->file_path); 261 | 262 | /** Récursivité **/ 263 | ExtractFolderFiles(current_image,current_entry,windows_folder_path,output_apple_single); 264 | } 265 | 266 | /** Libération mémoire **/ 267 | free(windows_folder_path); 268 | } 269 | 270 | /** 271 | * Writes a file to disk 272 | * 273 | * @brief CreateOutputFile 274 | * @param current_file 275 | * @param output_directory_path 276 | * @return 277 | */ 278 | static int CreateOutputFile(struct prodos_file *current_file, char *output_directory_path, bool output_apple_single) 279 | { 280 | int error; 281 | char directory_path[1024]; 282 | char file_data_path[1024]; 283 | char file_resource_path[1024]; 284 | char file_information_path[1024]; 285 | 286 | /* Création du répertoire de base */ 287 | error = os_CreateDirectory(output_directory_path); 288 | if(error) 289 | { 290 | logf_error(" Error : Can't create output folder '%s'.\n",output_directory_path); 291 | return(1); 292 | } 293 | strcpy(directory_path,output_directory_path); 294 | 295 | if(strlen(directory_path) > 0 && strncmp(&directory_path[strlen(directory_path)-1], FOLDER_CHARACTER, 1)) 296 | strcat(directory_path,FOLDER_CHARACTER); 297 | 298 | // Data file path 299 | strcpy(file_data_path,directory_path); 300 | strcat(file_data_path,current_file->entry->file_name_case); 301 | 302 | // Append the file type and auxtype extension 303 | if (!output_apple_single) 304 | { 305 | char extension[7]; 306 | strcat(file_data_path, "#"); 307 | sprintf(extension, "%02hhX%04hX", current_file->entry->file_type, current_file->entry->file_aux_type); 308 | strcat(file_data_path, extension); 309 | } 310 | 311 | // ResourceFork path 312 | strcpy(file_resource_path,file_data_path); 313 | strcat(file_resource_path,"_ResourceFork.bin"); 314 | 315 | /**********************************/ 316 | /** Création du Fichier : Data **/ 317 | /**********************************/ 318 | 319 | if (output_apple_single) 320 | { 321 | struct as_from_prodos as_file = ASFromProdosFile(current_file); 322 | error = CreateBinaryFile(file_data_path, as_file.data, as_file.length); 323 | } 324 | else 325 | error = CreateBinaryFile(file_data_path,current_file->data,current_file->data_length); 326 | 327 | if(error) 328 | { 329 | logf_error(" Error : Can't create file '%s' on disk at location '%s'.\n",current_file->entry->file_name_case,file_data_path); 330 | return(1); 331 | } 332 | 333 | /** Ajustement des Dates **/ 334 | os_SetFileCreationModificationDate(file_data_path,current_file->entry); 335 | 336 | #ifdef IS_WINDOWS 337 | /** Change la visibilité du fichier **/ 338 | os_SetFileAttribute( 339 | file_data_path, ((current_file->entry->access & 0x04) == 0x04) ? SET_FILE_HIDDEN : SET_FILE_VISIBLE 340 | ); 341 | #endif 342 | 343 | /** Ajoute des informations du fichier dans le fichier FileInformation.txt **/ 344 | 345 | if (!output_apple_single) 346 | { 347 | strcpy(file_information_path,directory_path); 348 | strcat(file_information_path,"_FileInformation.txt"); 349 | SetFileInformation(file_information_path,current_file); 350 | } 351 | 352 | /**************************************/ 353 | /** Création du Fichier : Resource **/ 354 | /**************************************/ 355 | if(current_file->resource_length > 0) 356 | { 357 | error = CreateBinaryFile(file_resource_path,current_file->resource,current_file->resource_length); 358 | if(error) 359 | { 360 | logf_error(" Error : Can't create resource file '%s' on disk at location '%s'.\n",current_file->entry->file_name_case,file_resource_path); 361 | return(1); 362 | } 363 | 364 | /** Ajustement des Dates **/ 365 | os_SetFileCreationModificationDate(file_resource_path,current_file->entry); 366 | 367 | #ifdef IS_WINDOWS 368 | /** Change la visibilité du fichier **/ 369 | os_SetFileAttribute( 370 | file_resource_path, 371 | ((current_file->entry->access & 0x04) == 0x04) ? SET_FILE_HIDDEN : SET_FILE_VISIBLE 372 | ); 373 | #endif 374 | } 375 | 376 | /* OK */ 377 | return(0); 378 | } 379 | 380 | 381 | /********************************************************************/ 382 | /* SetFileInformation() : Place les informations dans un fichier. */ 383 | /********************************************************************/ 384 | static void SetFileInformation(char *file_information_path, struct prodos_file *current_file) 385 | { 386 | FILE *fd; 387 | char *next_sep; 388 | int i, nb_line; 389 | char **line_tab; 390 | char file_name[1024]; 391 | char local_buffer[1024]; 392 | char folder_info1[256]; 393 | char folder_info2[256]; 394 | 395 | /* Folder Info */ 396 | for(i=0; i<18; i++) 397 | { 398 | sprintf(&folder_info1[2*i],"%02X",current_file->resource_finderinfo_1[i]); 399 | sprintf(&folder_info2[2*i],"%02X",current_file->resource_finderinfo_2[i]); 400 | } 401 | 402 | /** Prépare la ligne du fichier **/ 403 | sprintf(local_buffer,"%s=Type(%02X),AuxType(%04X),VersionCreate(%02X),MinVersion(%02X),Access(%02X),FolderInfo1(%s),FolderInfo2(%s)",current_file->entry->file_name_case, 404 | current_file->entry->file_type,current_file->entry->file_aux_type,current_file->entry->version_created,current_file->entry->min_version, 405 | current_file->entry->access,folder_info1,folder_info2); 406 | 407 | /** Charge en mémoire le fichier **/ 408 | line_tab = BuildUniqueListFromFile(file_information_path,&nb_line); 409 | if(line_tab == NULL) 410 | { 411 | /* Créer le fichier FileInformation */ 412 | CreateBinaryFile(file_information_path,(unsigned char *)local_buffer,(int)strlen(local_buffer)); 413 | 414 | #ifdef IS_WINDOWS 415 | /* Rendre le fichier invisible */ 416 | os_SetFileAttribute(file_information_path, SET_FILE_HIDDEN); 417 | return; 418 | #endif 419 | } 420 | 421 | #ifdef IS_WINDOWS 422 | /* Rendre le fichier visible */ 423 | os_SetFileAttribute(file_information_path, SET_FILE_VISIBLE); 424 | #endif 425 | 426 | /** Création du fichier **/ 427 | fd = fopen(file_information_path,"w"); 428 | if(fd == NULL) 429 | { 430 | mem_free_list(nb_line,line_tab); 431 | return; 432 | } 433 | 434 | /** Ajouts des lignes existantes **/ 435 | for(i=0; ientry->file_name_case)) 450 | logf("%s\n",line_tab[i]); 451 | } 452 | 453 | /* Nouvelle ligne */ 454 | logf("%s\n",local_buffer); 455 | 456 | /* Fermeture */ 457 | fclose(fd); 458 | 459 | /* Libération mémoire */ 460 | mem_free_list(nb_line,line_tab); 461 | 462 | /* Rendre le fichier invisible */ 463 | #ifdef IS_WINDOWS 464 | os_SetFileAttribute(file_information_path, SET_FILE_HIDDEN); 465 | #endif 466 | } 467 | 468 | /***********************************************************************/ 469 | -------------------------------------------------------------------------------- /Src/Prodos_Extract.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Extract.h : Header pour la gestion des commandes EXTRACT. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | 11 | void ExtractOneFile(struct prodos_image *, char *, char *, bool); 12 | void ExtractFolderFiles(struct prodos_image *, struct file_descriptive_entry *, char *, bool); 13 | void ExtractVolumeFiles(struct prodos_image *, char *, bool); 14 | 15 | /***********************************************************************/ 16 | -------------------------------------------------------------------------------- /Src/Prodos_Move.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Move.c : Module pour la gestion des commandes MOVE. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if IS_WINDOWS 15 | #include 16 | #endif 17 | 18 | #include "Dc_Shared.h" 19 | #include "Dc_Prodos.h" 20 | #include "Dc_Memory.h" 21 | #include "os/os.h" 22 | #include "Prodos_Create.h" 23 | #include "Prodos_Move.h" 24 | #include "log.h" 25 | 26 | static int MoveProdosFileToFolder(struct prodos_image *,struct file_descriptive_entry *,struct file_descriptive_entry *); 27 | static int MoveProdosFolderToFolder(struct prodos_image *,struct file_descriptive_entry *,struct file_descriptive_entry *); 28 | static void ChangeDirectoryEntriesDepth(struct file_descriptive_entry *,int); 29 | static void ChangeDirectoryEntriesPath(struct file_descriptive_entry *,char *,char *); 30 | static char *ChangeEntryPath(char *,char *,char *); 31 | 32 | 33 | /*********************************************************/ 34 | /* MoveProdosFile() : Déplacement d'un fichier Prodos. */ 35 | /*********************************************************/ 36 | void MoveProdosFile(struct prodos_image *current_image, char *prodos_file_path, char *target_folder_path) 37 | { 38 | int is_volume_header, error; 39 | struct file_descriptive_entry *current_entry; 40 | struct file_descriptive_entry *target_folder; 41 | 42 | /* Recherche l'entrée Prodos */ 43 | current_entry = GetProdosFile(current_image,prodos_file_path); 44 | if(current_entry == NULL) 45 | { 46 | logf_error(" Error : Invalid Prodos File path '%s'.\n",prodos_file_path); 47 | return; 48 | } 49 | 50 | /** Recherche le dossier Prodos Cible où déplacer le fichier **/ 51 | target_folder = BuildProdosFolderPath( 52 | current_image, 53 | target_folder_path, 54 | &is_volume_header, 55 | current_entry->lowercase, 56 | 0 57 | ); 58 | 59 | if(target_folder == NULL && is_volume_header == 0) 60 | return; 61 | 62 | /** Déplace le fichier dans un Dossier existant ou à la Racine du volume **/ 63 | error = MoveProdosFileToFolder(current_image,target_folder,current_entry); 64 | if(error) 65 | return; 66 | 67 | /** Ecrit le fichier Image **/ 68 | error = UpdateProdosImage(current_image); 69 | } 70 | 71 | 72 | /***********************************************************/ 73 | /* MoveProdosFolder() : Déplacement d'un dossier Prodos. */ 74 | /***********************************************************/ 75 | void MoveProdosFolder(struct prodos_image *current_image, char *prodos_folder_path, char *target_folder_path) 76 | { 77 | int is_volume_header, error; 78 | struct file_descriptive_entry *current_entry; 79 | struct file_descriptive_entry *target_folder; 80 | 81 | /* Recherche le dossier Prodos (on interdit le Nom du Volume) */ 82 | current_entry = GetProdosFolder(current_image,prodos_folder_path,0); 83 | if(current_entry == NULL) 84 | { 85 | logf_error(" Error : Invalid Prodos Folder path '%s'.\n",prodos_folder_path); 86 | return; 87 | } 88 | 89 | /** Recherche le dossier Prodos Cible où déplacer le dossier **/ 90 | target_folder = BuildProdosFolderPath( 91 | current_image, 92 | target_folder_path, 93 | &is_volume_header, 94 | current_entry->lowercase, 95 | 0 96 | ); 97 | 98 | if(target_folder == NULL && is_volume_header == 0) 99 | return; 100 | 101 | /** Déplace le dossier dans un Dossier existant ou à la Racine du volume **/ 102 | error = MoveProdosFolderToFolder(current_image,target_folder,current_entry); 103 | if(error) 104 | return; 105 | 106 | /** Ecrit le fichier Image **/ 107 | error = UpdateProdosImage(current_image); 108 | } 109 | 110 | 111 | /**************************************************************************************/ 112 | /* MoveProdosFileToFolder() : Déplace un fichier dans un répertoire ou à la racine. */ 113 | /**************************************************************************************/ 114 | int MoveProdosFileToFolder(struct prodos_image *current_image, struct file_descriptive_entry *target_folder, struct file_descriptive_entry *current_file) 115 | { 116 | int i, error, target_offset, file_block_number, file_block_offset, entry_length, file_header_pointer, file_count; 117 | WORD directory_block_number, directory_header_pointer; 118 | BYTE directory_entry_number; 119 | char *new_path; 120 | unsigned char directory_block[BLOCK_SIZE]; 121 | unsigned char file_entry_block[BLOCK_SIZE]; 122 | 123 | /** Vérifie que ce nom de fichier ne correspond pas déjà à un nom de fichier/dossier **/ 124 | if(target_folder == NULL) 125 | { 126 | /* Vérification à la racine du Volume */ 127 | for(i=0; inb_file; i++) 128 | if(!my_stricmp(current_file->file_name_case,current_image->tab_file[i]->file_name)) 129 | { 130 | logf_error(" Error : Invalid target location. A file already exist with the same name '%s'.\n",current_image->tab_file[i]->file_name); 131 | return(1); 132 | } 133 | for(i=0; inb_directory; i++) 134 | if(!my_stricmp(current_file->file_name_case,current_image->tab_directory[i]->file_name)) 135 | { 136 | logf_error(" Error : Invalid target location. A folder already exist with the same name '%s'.\n",current_image->tab_directory[i]->file_name); 137 | return(1); 138 | } 139 | } 140 | else 141 | { 142 | /* Vérification dans le dossier */ 143 | for(i=0; inb_file; i++) 144 | if(!my_stricmp(current_file->file_name_case,target_folder->tab_file[i]->file_name)) 145 | { 146 | logf_error(" Error : Invalid target location. A file already exist with the same name '%s'.\n",target_folder->tab_file[i]->file_name); 147 | return(1); 148 | } 149 | for(i=0; inb_directory; i++) 150 | if(!my_stricmp(current_file->file_name_case,target_folder->tab_directory[i]->file_name)) 151 | { 152 | logf_error(" Error : Invalid target location. A folder already exist with the same name '%s'.\n",target_folder->tab_directory[i]->file_name); 153 | return(1); 154 | } 155 | } 156 | 157 | /** Recherche d'une entrée libre dans le répertoire **/ 158 | error = AllocateFolderEntry(current_image,target_folder,&directory_block_number,&directory_entry_number,&directory_header_pointer); 159 | if(error) 160 | return(2); 161 | 162 | /*** Recopie les données de l'entrée du Fichier dans cette entrée ***/ 163 | entry_length = 0x27; 164 | 165 | /* Récupère les données de l'entrée */ 166 | file_block_number = current_file->block_location; 167 | file_block_offset = current_file->entry_offset; 168 | GetBlockData(current_image,file_block_number,&file_entry_block[0]); 169 | file_header_pointer = GetWordValue(file_entry_block,file_block_offset+0x25); 170 | 171 | /* Récupère les données du dossier cible */ 172 | target_offset = 4 + (directory_entry_number-1)*entry_length; 173 | GetBlockData(current_image,directory_block_number,&directory_block[0]); 174 | 175 | /** Copie les données de l'entrée dans le nouveau dossier **/ 176 | memcpy(&directory_block[target_offset],&file_entry_block[file_block_offset],entry_length); 177 | /* Modifie le Header Pointer */ 178 | SetWordValue(directory_block,target_offset+0x25,directory_header_pointer); 179 | /* Ecrit les données */ 180 | SetBlockData(current_image,directory_block_number,&directory_block[0]); 181 | 182 | /** Efface l'entrée de l'ancien Dossier **/ 183 | memset(&file_entry_block[file_block_offset],0,entry_length); 184 | SetBlockData(current_image,file_block_number,&file_entry_block[0]); 185 | 186 | /* Modifie le nombre d'entrées valides du Target Folder : +1 */ 187 | GetBlockData(current_image,directory_header_pointer,&directory_block[0]); 188 | file_count = GetWordValue(directory_block,0x25); 189 | SetWordValue(directory_block,0x25,(WORD)(file_count+1)); 190 | SetBlockData(current_image,directory_header_pointer,&directory_block[0]); 191 | 192 | /* Modifie le nombre d'entrées valides de l'ancien Folder : -1 */ 193 | GetBlockData(current_image,file_header_pointer,&directory_block[0]); 194 | file_count = GetWordValue(directory_block,0x25); 195 | SetWordValue(directory_block,0x25,(WORD)(file_count-1)); 196 | SetBlockData(current_image,file_header_pointer,&directory_block[0]); 197 | 198 | /***************************************/ 199 | /*** Met à jour la structure mémoire ***/ 200 | /** Met à jour le Dossier source (-1 fichier) **/ 201 | if(current_file->parent_directory == NULL) 202 | UpdateEntryTable(UPDATE_REMOVE,¤t_image->nb_file,¤t_image->tab_file,current_file); 203 | else 204 | UpdateEntryTable(UPDATE_REMOVE,¤t_file->parent_directory->nb_file,¤t_file->parent_directory->tab_file,current_file); 205 | 206 | /** Met à jour le Dossier Cible (+1 fichier) **/ 207 | if(target_folder == NULL) 208 | error = UpdateEntryTable(UPDATE_ADD,¤t_image->nb_file,¤t_image->tab_file,current_file); 209 | else 210 | error = UpdateEntryTable(UPDATE_ADD,&target_folder->nb_file,&target_folder->tab_file,current_file); 211 | if(error) 212 | { 213 | logf_error(" Error : Memory allocation impossible.\n"); 214 | return(1); 215 | } 216 | 217 | /** Met à jour l'entrée en mémoire **/ 218 | /* Nouveau Path */ 219 | new_path = (char *) calloc((target_folder==NULL)?1+strlen(current_image->volume_header->volume_name_case)+1+strlen(current_file->file_name_case)+1: 220 | strlen(target_folder->file_path)+1+strlen(current_file->file_name_case)+1, 221 | sizeof(char)); 222 | if(new_path == NULL) 223 | { 224 | logf_error(" Error : Memory allocation impossible.\n"); 225 | return(1); 226 | } 227 | if(target_folder == NULL) 228 | sprintf(new_path,"/%s/%s",current_image->volume_header->volume_name_case,current_file->file_name_case); 229 | else 230 | sprintf(new_path,"%s/%s",target_folder->file_path,current_file->file_name_case); 231 | if(current_file->file_path) 232 | free(current_file->file_path); 233 | current_file->file_path = new_path; 234 | 235 | /* Nouveau Header Pointer Block */ 236 | current_file->header_pointer_block = directory_header_pointer; 237 | 238 | /* Nouvelle profondeur (1=racine -> N) */ 239 | current_file->depth = (target_folder == NULL) ? 1 : target_folder->depth + 1; 240 | 241 | /* Nouvelle position dans un Folder */ 242 | current_file->block_location = directory_block_number; 243 | current_file->entry_offset = target_offset; 244 | 245 | /* Nouveau Parent Directory */ 246 | current_file->parent_directory = target_folder; 247 | 248 | /* OK */ 249 | return(0); 250 | } 251 | 252 | 253 | /****************************************************************************************/ 254 | /* MoveProdosFolderToFolder() : Déplace un dossier dans un répertoire ou à la racine. */ 255 | /****************************************************************************************/ 256 | int MoveProdosFolderToFolder(struct prodos_image *current_image, struct file_descriptive_entry *target_folder, struct file_descriptive_entry *current_folder) 257 | { 258 | int i, error, target_offset, file_block_number, file_block_offset, entry_length, file_header_pointer, file_count, depth_delta; 259 | WORD directory_block_number, directory_header_pointer; 260 | BYTE directory_entry_number; 261 | char old_path[2048]; 262 | char new_path[2048]; 263 | unsigned char directory_block[BLOCK_SIZE]; 264 | unsigned char file_entry_block[BLOCK_SIZE]; 265 | 266 | /* Ecart de profondeur (1=racine->N) */ 267 | depth_delta = ((target_folder == NULL) ? 1 : target_folder->depth+1) - (current_folder->depth); 268 | 269 | /* Translation de Chemin */ 270 | strcpy(old_path,current_folder->file_path); 271 | if(target_folder == NULL) 272 | sprintf(new_path,"/%s/%s",current_image->volume_header->volume_name_case,current_folder->file_name_case); 273 | else 274 | sprintf(new_path,"%s/%s",target_folder->file_path,current_folder->file_name_case); 275 | 276 | /* On ne peut pas déplacer un Dossier au même endroit */ 277 | if(!my_stricmp(old_path,new_path)) 278 | { 279 | logf_error(" Error : Invalid target location. The Folder is moved at the same location : '%s'.\n",target_folder->file_name_case); 280 | return(1); 281 | } 282 | /* On ne peut pas déplacer un Dossier sous lui-même */ 283 | if(strlen(new_path) > strlen(old_path)) 284 | if(!my_strnicmp(old_path,new_path,strlen(old_path))) 285 | { 286 | logf_error(" Error : Invalid target location. The Folder is moved under itself : '%s'.\n",target_folder->file_name_case); 287 | return(1); 288 | } 289 | 290 | /** Vérifie que ce nom de fichier ne correspond pas déjà à un nom de fichier/dossier **/ 291 | if(target_folder == NULL) 292 | { 293 | /* Vérification à la racine du Volume */ 294 | for(i=0; inb_file; i++) 295 | if(!my_stricmp(current_folder->file_name_case,current_image->tab_file[i]->file_name)) 296 | { 297 | logf_error(" Error : Invalid target location. A file already exist with the same name '%s'.\n",current_image->tab_file[i]->file_name); 298 | return(1); 299 | } 300 | for(i=0; inb_directory; i++) 301 | if(!my_stricmp(current_folder->file_name_case,current_image->tab_directory[i]->file_name)) 302 | { 303 | logf_error(" Error : Invalid target location. A folder already exist with the same name '%s'.\n",current_image->tab_directory[i]->file_name); 304 | return(1); 305 | } 306 | } 307 | else 308 | { 309 | /* Vérification dans le dossier */ 310 | for(i=0; inb_file; i++) 311 | if(!my_stricmp(current_folder->file_name_case,target_folder->tab_file[i]->file_name)) 312 | { 313 | logf_error(" Error : Invalid target location. A file already exist with the same name '%s'.\n",target_folder->tab_file[i]->file_name); 314 | return(1); 315 | } 316 | for(i=0; inb_directory; i++) 317 | if(!my_stricmp(current_folder->file_name_case,target_folder->tab_directory[i]->file_name)) 318 | { 319 | logf_error(" Error : Invalid target location. A folder already exist with the same name '%s'.\n",target_folder->tab_directory[i]->file_name); 320 | return(1); 321 | } 322 | } 323 | 324 | /** Recherche d'une entrée libre dans le répertoire **/ 325 | error = AllocateFolderEntry(current_image,target_folder,&directory_block_number,&directory_entry_number,&directory_header_pointer); 326 | if(error) 327 | return(2); 328 | 329 | /*** Recopie les données de l'entrée du Fichier dans cette entrée ***/ 330 | entry_length = 0x27; 331 | 332 | /* Récupère les données de l'entrée */ 333 | file_block_number = current_folder->block_location; 334 | file_block_offset = current_folder->entry_offset; 335 | GetBlockData(current_image,file_block_number,&file_entry_block[0]); 336 | file_header_pointer = GetWordValue(file_entry_block,file_block_offset+FILE_HEADERPOINTER_OFFSET); 337 | 338 | /* Récupère les données du dossier cible */ 339 | target_offset = 4 + (directory_entry_number-1)*entry_length; 340 | GetBlockData(current_image,directory_block_number,&directory_block[0]); 341 | 342 | /** Copie les données de l'entrée dans le nouveau dossier **/ 343 | memcpy(&directory_block[target_offset],&file_entry_block[file_block_offset],entry_length); 344 | /* Modifie le Header Pointer */ 345 | SetWordValue(directory_block,target_offset+FILE_HEADERPOINTER_OFFSET,directory_header_pointer); 346 | /* Ecrit les données */ 347 | SetBlockData(current_image,directory_block_number,&directory_block[0]); 348 | 349 | /** Efface l'entrée de l'ancien Dossier **/ 350 | memset(&file_entry_block[file_block_offset],0,entry_length); 351 | SetBlockData(current_image,file_block_number,&file_entry_block[0]); 352 | 353 | /* Modifie le nombre d'entrées valides du Target Folder : +1 */ 354 | GetBlockData(current_image,directory_header_pointer,&directory_block[0]); 355 | file_count = GetWordValue(directory_block,DIRECTORY_FILECOUNT_OFFSET); 356 | SetWordValue(directory_block,DIRECTORY_FILECOUNT_OFFSET,(WORD)(file_count+1)); 357 | SetBlockData(current_image,directory_header_pointer,&directory_block[0]); 358 | 359 | /* Modifie le nombre d'entrées valides de l'ancien Folder : -1 */ 360 | GetBlockData(current_image,file_header_pointer,&directory_block[0]); 361 | file_count = GetWordValue(directory_block,DIRECTORY_FILECOUNT_OFFSET); 362 | SetWordValue(directory_block,DIRECTORY_FILECOUNT_OFFSET,(WORD)(file_count-1)); 363 | SetBlockData(current_image,file_header_pointer,&directory_block[0]); 364 | 365 | /** Modifie le Parent Pointer Block et le Parent Entry du dossier que l'on déplace **/ 366 | GetBlockData(current_image,current_folder->key_pointer_block,&directory_block[0]); 367 | SetWordValue(directory_block,DIRECTORY_PARENTPOINTERBLOCK_OFFSET,(WORD)directory_block_number); 368 | directory_block[DIRECTORY_PARENTENTRY_OFFSET] = (BYTE) directory_entry_number; 369 | SetBlockData(current_image,current_folder->key_pointer_block,&directory_block[0]); 370 | 371 | /***************************************/ 372 | /*** Met à jour la structure mémoire ***/ 373 | /** Met à jour le Dossier source (-1 fichier) **/ 374 | if(current_folder->parent_directory == NULL) 375 | UpdateEntryTable(UPDATE_REMOVE,¤t_image->nb_file,¤t_image->tab_file,current_folder); 376 | else 377 | UpdateEntryTable(UPDATE_REMOVE,¤t_folder->parent_directory->nb_file,¤t_folder->parent_directory->tab_file,current_folder); 378 | 379 | /** Met à jour le Dossier Cible (+1 fichier) **/ 380 | if(target_folder == NULL) 381 | error = UpdateEntryTable(UPDATE_ADD,¤t_image->nb_file,¤t_image->tab_file,current_folder); 382 | else 383 | error = UpdateEntryTable(UPDATE_ADD,&target_folder->nb_file,&target_folder->tab_file,current_folder); 384 | if(error) 385 | { 386 | logf_error(" Error : Memory allocation impossible.\n"); 387 | return(1); 388 | } 389 | 390 | /** Met à jour l'entrée en mémoire **/ 391 | 392 | /* Nouveau Header Pointer Block */ 393 | current_folder->header_pointer_block = directory_header_pointer; 394 | 395 | /* Nouvelle position dans un Folder */ 396 | current_folder->block_location = directory_block_number; 397 | current_folder->entry_offset = target_offset; 398 | 399 | /* Nouveau Parent Directory */ 400 | current_folder->parent_directory = target_folder; 401 | 402 | /** Modifie toutes les entrées de ce Répertoire **/ 403 | /* Depth */ 404 | ChangeDirectoryEntriesDepth(current_folder,depth_delta); 405 | 406 | /* Path */ 407 | ChangeDirectoryEntriesPath(current_folder,old_path,new_path); 408 | 409 | /* OK */ 410 | return(0); 411 | } 412 | 413 | 414 | /*********************************************************************************/ 415 | /* ChangeDirectoryEntriesDepth() : Change le niveau de profondeur des entrées. */ 416 | /*********************************************************************************/ 417 | static void ChangeDirectoryEntriesDepth(struct file_descriptive_entry *current_folder, int depth_delta) 418 | { 419 | int i; 420 | 421 | /* Le dossier lui même */ 422 | current_folder->depth += depth_delta; 423 | 424 | /* Les File du dossier */ 425 | for(i=0; inb_file; i++) 426 | current_folder->tab_file[i]->depth += depth_delta; 427 | 428 | /* Les Folder du dossier (récursivité) */ 429 | for(i=0; inb_directory; i++) 430 | ChangeDirectoryEntriesDepth(current_folder->tab_directory[i],depth_delta); 431 | } 432 | 433 | 434 | /******************************************************************/ 435 | /* ChangeDirectoryEntriesPath() : Change le chemin des entrées. */ 436 | /******************************************************************/ 437 | static void ChangeDirectoryEntriesPath(struct file_descriptive_entry *current_folder, char *old_path, char *new_path) 438 | { 439 | int i; 440 | 441 | /* Le dossier lui même */ 442 | current_folder->file_path = ChangeEntryPath(current_folder->file_path,old_path,new_path); 443 | 444 | /* Les File du dossier */ 445 | for(i=0; inb_file; i++) 446 | current_folder->tab_file[i]->file_path = ChangeEntryPath(current_folder->tab_file[i]->file_path,old_path,new_path); 447 | 448 | /* Les Folder du dossier (récursivité) */ 449 | for(i=0; inb_directory; i++) 450 | ChangeDirectoryEntriesPath(current_folder->tab_directory[i],old_path,new_path); 451 | } 452 | 453 | 454 | /*****************************************************/ 455 | /* ChangeEntryPath() : Création du nouveau chemin. */ 456 | /*****************************************************/ 457 | static char *ChangeEntryPath(char *current_file_path, char *old_path, char *new_path) 458 | { 459 | int length; 460 | char *new_file_path; 461 | 462 | /* Taille du chemin */ 463 | length = strlen(new_path) + strlen(¤t_file_path[strlen(old_path)]); 464 | 465 | /* Allocation mémoire */ 466 | new_file_path = (char *) calloc(length+1,sizeof(char)); 467 | if(new_file_path == NULL) 468 | { 469 | logf_error(" Error : Memory allocation impossible.\n"); 470 | return(current_file_path); 471 | } 472 | 473 | /* Nouveau chemin */ 474 | strcpy(new_file_path,new_path); 475 | strcat(new_file_path,¤t_file_path[strlen(old_path)]); 476 | 477 | /* Libératioon mémoire ancien chemin */ 478 | free(current_file_path); 479 | 480 | /* Renvoi le chemin */ 481 | return(new_file_path); 482 | } 483 | 484 | /***********************************************************************/ 485 | -------------------------------------------------------------------------------- /Src/Prodos_Move.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Move.h : Header pour la gestion des commandes MOVE. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /***********************************************************************/ 8 | 9 | void MoveProdosFile(struct prodos_image *,char *,char *); 10 | void MoveProdosFolder(struct prodos_image *,char *,char *); 11 | 12 | /***********************************************************************/ 13 | -------------------------------------------------------------------------------- /Src/Prodos_Rename.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Rename.c : Module pour la gestion des commandes RENAME. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if IS_WINDOWS 15 | #include 16 | #endif 17 | 18 | #include "os/os.h" 19 | #include "Dc_Shared.h" 20 | #include "Dc_Prodos.h" 21 | #include "Prodos_Rename.h" 22 | #include "log.h" 23 | 24 | /********************************************************/ 25 | /* RenameProdosFile() : Renomage d'un fichier Prodos. */ 26 | /********************************************************/ 27 | void RenameProdosFile(struct prodos_image *current_image, char *prodos_file_path, char *new_file_name) 28 | { 29 | char upper_case[256]; 30 | WORD name_case, now_date, now_time; 31 | BYTE storage_length; 32 | int i, is_valid, error; 33 | unsigned char name_length; 34 | struct file_descriptive_entry *current_entry; 35 | unsigned char directory_block[BLOCK_SIZE]; 36 | 37 | /* Recherche l'entrée Prodos */ 38 | current_entry = GetProdosFile(current_image,prodos_file_path); 39 | if(current_entry == NULL) 40 | { 41 | logf_error(" Error : Invalid Prodos File path '%s'.\n",prodos_file_path); 42 | return; 43 | } 44 | 45 | /* Vérification du nouveau nom */ 46 | is_valid = CheckProdosName(new_file_name); 47 | if(is_valid == 0) 48 | { 49 | logf_error(" Error : Invalid Prodos name '%s'.\n",new_file_name); 50 | return; 51 | } 52 | 53 | /* On vérifie si ce n'est pas le même nom */ 54 | if(!strcmp(current_entry->file_name_case,new_file_name)) 55 | return; 56 | 57 | /* Nom en majuscule */ 58 | my_strcpy(upper_case,sizeof(upper_case),new_file_name); 59 | for(i=0; i<(int)strlen(upper_case); i++) 60 | upper_case[i] = toupper(upper_case[i]); 61 | 62 | /* 16 bit décrivant la case */ 63 | name_case = BuildProdosCase(new_file_name); 64 | 65 | /* Longueur du nom */ 66 | name_length = (unsigned char) strlen(new_file_name); 67 | 68 | /* Storage Type + Name length */ 69 | storage_length = (current_entry->storage_type << 4) | name_length; 70 | 71 | /* Date actuelle */ 72 | GetCurrentDate(&now_date,&now_time); 73 | 74 | /*****************************************************/ 75 | /** On va modifier le nom dans la structure mémoire **/ 76 | current_entry->name_length = (int) name_length; 77 | strcpy(current_entry->file_name,upper_case); 78 | strcpy(current_entry->file_name_case,new_file_name); 79 | current_entry->lowercase = name_case; 80 | 81 | /***********************************/ 82 | /** On va modifier l'image disque **/ 83 | /* Directory Block */ 84 | GetBlockData(current_image,current_entry->block_location,&directory_block[0]); 85 | 86 | /** Place les informations dans le Directory **/ 87 | memcpy(&directory_block[current_entry->entry_offset+FILE_STORAGETYPE_OFFSET],&storage_length,sizeof(BYTE)); 88 | memcpy(&directory_block[current_entry->entry_offset+FILE_NAME_OFFSET],upper_case,strlen(upper_case)); 89 | memcpy(&directory_block[current_entry->entry_offset+FILE_LOWERCASE_OFFSET],&name_case,sizeof(WORD)); 90 | 91 | /* Modifie le block Directory */ 92 | SetBlockData(current_image,current_entry->block_location,&directory_block[0]); 93 | 94 | /** Ecrit le fichier **/ 95 | error = UpdateProdosImage(current_image); 96 | } 97 | 98 | 99 | /**********************************************************/ 100 | /* RenameProdosFolder() : Renomage d'un dossier Prodos. */ 101 | /**********************************************************/ 102 | void RenameProdosFolder(struct prodos_image *current_image, char *prodos_folder_path, char *new_folder_name) 103 | { 104 | char upper_case[256]; 105 | WORD name_case, now_date, now_time; 106 | BYTE storage_length; 107 | int i, is_valid, error; 108 | unsigned char name_length; 109 | struct file_descriptive_entry *current_entry; 110 | unsigned char directory_block[BLOCK_SIZE]; 111 | 112 | /* Recherche le dossier Prodos */ 113 | current_entry = GetProdosFolder(current_image,prodos_folder_path,0); 114 | if(current_entry == NULL) 115 | { 116 | logf_error(" Error : Invalid Prodos Folder path '%s'.\n",prodos_folder_path); 117 | return; 118 | } 119 | 120 | /* Vérification du nouveau nom */ 121 | is_valid = CheckProdosName(new_folder_name); 122 | if(is_valid == 0) 123 | { 124 | logf_error(" Error : Invalid Prodos name '%s'.\n",new_folder_name); 125 | return; 126 | } 127 | 128 | /* On vérifie si ce n'est pas le même nom */ 129 | if(!strcmp(current_entry->file_name_case,new_folder_name)) 130 | return; 131 | 132 | /* Nom en majuscule */ 133 | strcpy(upper_case,new_folder_name); 134 | for(i=0; i<(int)strlen(upper_case); i++) 135 | upper_case[i] = toupper(upper_case[i]); 136 | 137 | /* 16 bit décrivant la case */ 138 | name_case = BuildProdosCase(new_folder_name); 139 | 140 | /* Longueur du nom */ 141 | name_length = (unsigned char) strlen(new_folder_name); 142 | 143 | /* Storage Type + Name length */ 144 | storage_length = (current_entry->storage_type << 4) | name_length; 145 | 146 | /* Date actuelle */ 147 | GetCurrentDate(&now_date,&now_time); 148 | 149 | /*****************************************************/ 150 | /** On va modifier le nom dans la structure mémoire **/ 151 | current_entry->name_length = (int) name_length; 152 | strcpy(current_entry->file_name,upper_case); 153 | strcpy(current_entry->file_name_case,new_folder_name); 154 | current_entry->lowercase = name_case; 155 | 156 | /***********************************/ 157 | /** On va modifier l'image disque **/ 158 | /** Directory Block **/ 159 | GetBlockData(current_image,current_entry->block_location,&directory_block[0]); 160 | 161 | /** Place les informations dans le Directory **/ 162 | memcpy(&directory_block[current_entry->entry_offset+FILE_STORAGETYPE_OFFSET],&storage_length,sizeof(BYTE)); 163 | memcpy(&directory_block[current_entry->entry_offset+FILE_NAME_OFFSET],upper_case,strlen(upper_case)); 164 | memcpy(&directory_block[current_entry->entry_offset+FILE_LOWERCASE_OFFSET],&name_case,sizeof(WORD)); 165 | 166 | /* Modifie le block Directory */ 167 | SetBlockData(current_image,current_entry->block_location,&directory_block[0]); 168 | 169 | /** Sub-Directory Block **/ 170 | GetBlockData(current_image,current_entry->key_pointer_block,&directory_block[0]); 171 | 172 | /** Place les informations dans le Sub-Directory **/ 173 | memcpy(&directory_block[DIRECTORY_STORAGETYPE_OFFSET],&storage_length,sizeof(BYTE)); 174 | memcpy(&directory_block[DIRECTORY_NAME_OFFSET],upper_case,strlen(upper_case)); 175 | memcpy(&directory_block[DIRECTORY_LOWERCASE_OFFSET],&name_case,sizeof(WORD)); 176 | 177 | /* Modifie le block Sub-Directory */ 178 | SetBlockData(current_image,current_entry->key_pointer_block,&directory_block[0]); 179 | 180 | /** Ecrit le fichier **/ 181 | error = UpdateProdosImage(current_image); 182 | } 183 | 184 | 185 | /*******************************************************/ 186 | /* RenameProdosVolume() : Renomage du volume Prodos. */ 187 | /*******************************************************/ 188 | void RenameProdosVolume(struct prodos_image *current_image, char *new_volume_name) 189 | { 190 | char upper_case[256]; 191 | WORD name_case, now_date, now_time; 192 | BYTE storage_length; 193 | int i, is_valid, error; 194 | unsigned char name_length; 195 | unsigned char volume_block[BLOCK_SIZE]; 196 | 197 | /* Vérification du nouveau nom */ 198 | is_valid = CheckProdosName(new_volume_name); 199 | if(is_valid == 0) 200 | { 201 | logf_error(" Error : Invalid Prodos name '%s'.\n",new_volume_name); 202 | return; 203 | } 204 | 205 | /* On vérifie si ce n'est pas le même nom */ 206 | if(!strcmp(current_image->volume_header->volume_name_case,new_volume_name)) 207 | return; 208 | 209 | /* Nom en majuscule */ 210 | strcpy(upper_case,new_volume_name); 211 | for(i=0; i<(int)strlen(upper_case); i++) 212 | upper_case[i] = toupper(upper_case[i]); 213 | 214 | /* 16 bit décrivant la case */ 215 | name_case = BuildProdosCase(new_volume_name); 216 | 217 | /* Longueur du nom */ 218 | name_length = (unsigned char) strlen(new_volume_name); 219 | 220 | /* Storage Type + Name length */ 221 | storage_length = (current_image->volume_header->storage_type << 4) | name_length; 222 | 223 | /* Date actuelle */ 224 | GetCurrentDate(&now_date,&now_time); 225 | 226 | /*****************************************************/ 227 | /** On va modifier le nom dans la structure mémoire **/ 228 | current_image->volume_header->name_length = (int) name_length; 229 | strcpy(current_image->volume_header->volume_name,upper_case); 230 | strcpy(current_image->volume_header->volume_name_case,new_volume_name); 231 | current_image->volume_header->lowercase = name_case; 232 | GetProdosDate(now_date,¤t_image->volume_header->volume_modification_date); 233 | GetProdosTime(now_time,¤t_image->volume_header->volume_modification_time); 234 | 235 | /***********************************/ 236 | /** On va modifier l'image disque **/ 237 | /* Volume Block */ 238 | GetBlockData(current_image,2,&volume_block[0]); 239 | 240 | /** Place les informations dans le Volume Header **/ 241 | memcpy(&volume_block[VOLUME_STORAGETYPE_OFFSET],&storage_length,sizeof(BYTE)); 242 | memcpy(&volume_block[VOLUME_NAME_OFFSET],upper_case,strlen(upper_case)); 243 | memcpy(&volume_block[VOLUME_DATEMODIF_OFFSET],&now_date,sizeof(WORD)); 244 | memcpy(&volume_block[VOLUME_TIMEMODIF_OFFSET],&now_time,sizeof(WORD)); 245 | memcpy(&volume_block[VOLUME_LOWERCASE_OFFSET],&name_case,sizeof(WORD)); 246 | 247 | /* Modifie le block */ 248 | SetBlockData(current_image,2,&volume_block[0]); 249 | 250 | /** Ecrit le fichier **/ 251 | error = UpdateProdosImage(current_image); 252 | } 253 | 254 | /***********************************************************************/ 255 | -------------------------------------------------------------------------------- /Src/Prodos_Rename.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Rename.h : Header pour la gestion des commandes RENAME. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 7 | /***********************************************************************/ 8 | 9 | void RenameProdosFile(struct prodos_image *,char *,char *); 10 | void RenameProdosFolder(struct prodos_image *,char *,char *); 11 | void RenameProdosVolume(struct prodos_image *,char *); 12 | 13 | /***********************************************************************/ -------------------------------------------------------------------------------- /Src/Prodos_Source.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Source.c : Module pour la gestion des fichiers Source. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #if IS_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include "Dc_Shared.h" 18 | #include "os/os.h" 19 | #include "Prodos_Source.h" 20 | #include "log.h" 21 | 22 | #define TYPE_LINE_CODE 1 23 | #define TYPE_LINE_COMMENT 2 24 | #define TYPE_LINE_EMPTY 3 25 | #define TYPE_LINE_DATA 4 26 | 27 | #define ERR_MEMORY_ALLOC "Memory allocation impossible" 28 | #define ERR_LINE_FORMAT "Wrong line format" 29 | 30 | struct line 31 | { 32 | int type; 33 | 34 | char *label; 35 | char *opcode; 36 | char *operand; 37 | char *comment; 38 | 39 | int number; /* Numéro de la ligne dans le fichier */ 40 | int length; /* Taille de la ligne */ 41 | 42 | struct line *next; 43 | }; 44 | 45 | static unsigned char *BuildIndentBuffer(int,struct line **); 46 | static unsigned char *BuildOutdentBuffer(int,struct line **); 47 | static struct line **BuildLineTab(unsigned char *,int,int *); 48 | static struct line *BuildOneLine(unsigned char *,int); 49 | static int GetLineValue(char *,int,char **); 50 | static void mem_free_line(struct line *); 51 | 52 | /******************************************************************/ 53 | /* ClearFileHighBit() : Met à 0 le bit 7 des octets du fichier. */ 54 | /******************************************************************/ 55 | int ClearFileHighBit(char *file_path) 56 | { 57 | int i, result; 58 | int length; 59 | unsigned char *data; 60 | 61 | /** Chargement en mémoire du fichier **/ 62 | data = LoadBinaryFile(file_path,&length); 63 | if(data == NULL) 64 | { 65 | logf_error(" Error : Impossible to open file '%s'.\n",file_path); 66 | return(1); 67 | } 68 | 69 | /** Clear Bit 7 (on ne touche pas aux 0x20) **/ 70 | for(i=0; itype == TYPE_LINE_CODE || tab_line[i]->type == TYPE_LINE_DATA) 276 | { 277 | if((int)strlen(tab_line[i]->label) > label_length) 278 | label_length = strlen(tab_line[i]->label); 279 | 280 | if((int)strlen(tab_line[i]->opcode) > opcode_length && strlen(tab_line[i]->operand) > 0) 281 | opcode_length = strlen(tab_line[i]->opcode); 282 | 283 | if((int)strlen(tab_line[i]->comment) > comment_length) 284 | comment_length = strlen(tab_line[i]->comment); 285 | } 286 | 287 | if(tab_line[i]->type == TYPE_LINE_CODE) 288 | { 289 | if((int)strlen(tab_line[i]->operand) > operand_length) 290 | operand_length = strlen(tab_line[i]->operand); 291 | } 292 | else if(tab_line[i]->type == TYPE_LINE_DATA) 293 | { 294 | if((int)strlen(tab_line[i]->operand) > operand_data_length) 295 | operand_data_length = strlen(tab_line[i]->operand); 296 | } 297 | 298 | if(line_length < tab_line[i]->length) 299 | line_length = tab_line[i]->length; 300 | } 301 | operand_data_length = (operand_data_length > operand_length) ? operand_data_length : operand_length; 302 | if(line_length < (label_length + opcode_length + operand_data_length + comment_length)) 303 | line_length = (label_length + opcode_length + operand_data_length + comment_length); 304 | 305 | /* Allocation mémoire du fichier */ 306 | data = calloc(nb_line,line_length+1); 307 | if(data == NULL) 308 | { 309 | logf_error(" Error : Impossible to allocate memory to process the file.\n"); 310 | return(NULL); 311 | } 312 | 313 | /**********************************/ 314 | /** Création du fichier résultat **/ 315 | /**********************************/ 316 | for(i=0,offset=0; itype == TYPE_LINE_EMPTY) 319 | data[offset++] = '\n'; 320 | else if(tab_line[i]->type == TYPE_LINE_COMMENT) 321 | { 322 | memcpy(&data[offset],tab_line[i]->comment,strlen(tab_line[i]->comment)); 323 | offset += strlen(tab_line[i]->comment); 324 | data[offset++] = '\n'; 325 | } 326 | else if(tab_line[i]->type == TYPE_LINE_CODE || tab_line[i]->type == TYPE_LINE_DATA) 327 | { 328 | /** Label **/ 329 | length = strlen(tab_line[i]->label); 330 | memcpy(&data[offset],tab_line[i]->label,length); 331 | offset += length; 332 | 333 | /* Separator */ 334 | if(strlen(tab_line[i]->opcode) + strlen(tab_line[i]->operand) + strlen(tab_line[i]->comment) == 0) 335 | { 336 | data[offset++] = '\n'; 337 | continue; 338 | } 339 | for(j=length; jopcode); 346 | memcpy(&data[offset],tab_line[i]->opcode,length); 347 | offset += length; 348 | 349 | /* Separator */ 350 | if(strlen(tab_line[i]->operand) + strlen(tab_line[i]->comment) == 0) 351 | { 352 | data[offset++] = '\n'; 353 | continue; 354 | } 355 | for(j=length; joperand); 362 | memcpy(&data[offset],tab_line[i]->operand,length); 363 | offset += length; 364 | 365 | /* Separator */ 366 | if(strlen(tab_line[i]->comment) == 0) 367 | { 368 | data[offset++] = '\n'; 369 | continue; 370 | } 371 | if(tab_line[i]->type == TYPE_LINE_CODE) 372 | { 373 | /* L'opcode a débordé sur l'operand, qui n'était pas là */ 374 | if(strlen(tab_line[i]->operand) == 0 && (int) strlen(tab_line[i]->opcode) > opcode_length) 375 | { 376 | for(j=length+strlen(tab_line[i]->opcode)-opcode_length; jtype == TYPE_LINE_DATA && length < operand_length) 386 | { 387 | /* On met le commentaire des Data au même niveau que celui du code */ 388 | for(j=length; jcomment); 396 | memcpy(&data[offset],tab_line[i]->comment,length); 397 | offset += length; 398 | 399 | /* Ligne suivante */ 400 | data[offset++] = '\n'; 401 | } 402 | } 403 | data[offset] = '\0'; 404 | 405 | /* Renvoi le buffer */ 406 | return(data); 407 | } 408 | 409 | 410 | /****************************************************************************/ 411 | /* BuildOutdentBuffer() : Construction du buffer des lignes dé-indentées. */ 412 | /****************************************************************************/ 413 | static unsigned char *BuildOutdentBuffer(int nb_line, struct line **tab_line) 414 | { 415 | int i, length, offset, line_length; 416 | unsigned char *data; 417 | 418 | /** Détermine la taille max de la ligne **/ 419 | line_length = 0; 420 | for(i=0; itype == TYPE_LINE_CODE || tab_line[i]->type == TYPE_LINE_DATA) 423 | length = strlen(tab_line[i]->label) + 1 + strlen(tab_line[i]->opcode) + 1 + strlen(tab_line[i]->operand) + 1 + strlen(tab_line[i]->comment); 424 | else if(tab_line[i]->type == TYPE_LINE_COMMENT) 425 | length = strlen(tab_line[i]->comment) + 3; 426 | else 427 | length = 1; 428 | 429 | if(line_length < length) 430 | line_length = length; 431 | } 432 | 433 | /* Allocation mémoire du fichier */ 434 | data = calloc(nb_line,line_length+1); 435 | if(data == NULL) 436 | { 437 | logf_error(" Error : Impossible to allocate memory to process the file.\n"); 438 | return(NULL); 439 | } 440 | 441 | /**********************************/ 442 | /** Création du fichier résultat **/ 443 | /**********************************/ 444 | for(i=0,offset=0; itype == TYPE_LINE_EMPTY) 447 | data[offset++] = '\n'; 448 | else if(tab_line[i]->type == TYPE_LINE_COMMENT) 449 | { 450 | memcpy(&data[offset],tab_line[i]->comment,strlen(tab_line[i]->comment)); 451 | offset += strlen(tab_line[i]->comment); 452 | data[offset++] = '\n'; 453 | } 454 | else if(tab_line[i]->type == TYPE_LINE_CODE || tab_line[i]->type == TYPE_LINE_DATA) 455 | { 456 | /** Label **/ 457 | length = strlen(tab_line[i]->label); 458 | memcpy(&data[offset],tab_line[i]->label,length); 459 | offset += length; 460 | 461 | /* Separator */ 462 | data[offset++] = '\t'; 463 | 464 | /** Opcode **/ 465 | length = strlen(tab_line[i]->opcode); 466 | memcpy(&data[offset],tab_line[i]->opcode,length); 467 | offset += length; 468 | 469 | /* Separator */ 470 | data[offset++] = '\t'; 471 | 472 | /** Operand **/ 473 | length = strlen(tab_line[i]->operand); 474 | memcpy(&data[offset],tab_line[i]->operand,length); 475 | offset += length; 476 | 477 | /* Separator */ 478 | data[offset++] = '\t'; 479 | 480 | /** Comment **/ 481 | length = strlen(tab_line[i]->comment); 482 | memcpy(&data[offset],tab_line[i]->comment,length); 483 | offset += length; 484 | 485 | /* Compact EndOfLine */ 486 | while(offset > 0 && data[offset-1] == '\t') 487 | offset--; 488 | 489 | /* Ligne suivante */ 490 | data[offset++] = '\n'; 491 | } 492 | } 493 | data[offset] = '\0'; 494 | 495 | /* Renvoi le buffer */ 496 | return(data); 497 | } 498 | 499 | 500 | /*********************************************************************/ 501 | /* BuildLineTab() : Décode un fichier sous forme de ligne de code. */ 502 | /*********************************************************************/ 503 | static struct line **BuildLineTab(unsigned char *data, int length, int *nb_line_rtn) 504 | { 505 | unsigned char *end; 506 | unsigned char *begin; 507 | int i, nb_line; 508 | struct line *current_line; 509 | struct line **tab_line; 510 | 511 | /* Compte le nombre de ligne */ 512 | for(i=0,nb_line=1; inumber = line_nb; 574 | current_line->length = strlen((const char *) line) + 1; 575 | 576 | /* Ligne vide ? */ 577 | if(strlen((const char *) line) == 0) 578 | { 579 | current_line->type = TYPE_LINE_EMPTY; 580 | return(current_line); 581 | } 582 | 583 | /* Ligne commentaire */ 584 | if(line[0] == ';' || line[0] == '*') 585 | { 586 | current_line->type = TYPE_LINE_COMMENT; 587 | current_line->comment = strdup((const char *) line); 588 | if(current_line->comment == NULL) 589 | { 590 | logf_error(" Error processing line %d (%s) : %s\n",line_nb,line,ERR_MEMORY_ALLOC); 591 | mem_free_line(current_line); 592 | return(NULL); 593 | } 594 | return(current_line); 595 | } 596 | 597 | /*** Décodage des 4 zones ***/ 598 | current_line->type = TYPE_LINE_CODE; 599 | 600 | /** Label **/ 601 | offset = 0; 602 | length = GetLineValue((char *) &line[offset],0,¤t_line->label); 603 | if(length < 0) 604 | { 605 | logf_error(" Error processing line %d (%s) : %s\n",line_nb,line,(length == -1)?ERR_MEMORY_ALLOC:ERR_LINE_FORMAT); 606 | mem_free_line(current_line); 607 | return(NULL); 608 | } 609 | 610 | /** Opcode **/ 611 | offset += length; 612 | length = GetLineValue((char *) &line[offset],0,¤t_line->opcode); 613 | if(length < 0) 614 | { 615 | logf_error(" Error processing line %d (%s) : %s\n",line_nb,line,(length == -1)?ERR_MEMORY_ALLOC:ERR_LINE_FORMAT); 616 | mem_free_line(current_line); 617 | return(NULL); 618 | } 619 | 620 | /** Operand **/ 621 | offset += length; 622 | length = GetLineValue((char *) &line[offset],0,¤t_line->operand); 623 | if(length < 0) 624 | { 625 | logf_error(" Error processing line %d (%s) : %s\n",line_nb,line,(length == -1)?ERR_MEMORY_ALLOC:ERR_LINE_FORMAT); 626 | mem_free_line(current_line); 627 | return(NULL); 628 | } 629 | 630 | /** Comment **/ 631 | offset += length; 632 | length = GetLineValue((char *) &line[offset],1,¤t_line->comment); 633 | if(length < 0) 634 | { 635 | logf_error(" Error processing line %d (%s) : %s\n",line_nb,line,(length == -1)?ERR_MEMORY_ALLOC:ERR_LINE_FORMAT); 636 | mem_free_line(current_line); 637 | return(NULL); 638 | } 639 | 640 | /** Est-ce une ligne DATA ? **/ 641 | if(strlen(current_line->opcode) > 0) 642 | { 643 | for(i=0; data_tab[i] != NULL; i++) 644 | if(!my_stricmp(data_tab[i],current_line->opcode)) 645 | { 646 | current_line->type = TYPE_LINE_DATA; 647 | break; 648 | } 649 | } 650 | 651 | /* Renvoi la structure */ 652 | return(current_line); 653 | } 654 | 655 | 656 | /************************************************************/ 657 | /* GetLineValue() : Récupère une des valeurs de la ligne. */ 658 | /************************************************************/ 659 | static int GetLineValue(char *line_data, int is_comment, char **value_rtn) 660 | { 661 | int i, length; 662 | char *value; 663 | char *end_sep; 664 | 665 | /** Pas de valeur **/ 666 | if(line_data[0] == '\0') 667 | { 668 | /* Valeur */ 669 | value = strdup(""); 670 | if(value == NULL) 671 | return(-1); 672 | 673 | /* Longueur */ 674 | length = 0; 675 | } 676 | else if(line_data[0] == ';' && is_comment == 0) 677 | { 678 | /* Valeur */ 679 | value = strdup(""); 680 | if(value == NULL) 681 | return(-1); 682 | 683 | /* Longueur */ 684 | length = 0; 685 | } 686 | else if(line_data[0] == ';' && is_comment == 1) 687 | { 688 | /* Valeur */ 689 | value = strdup(line_data); 690 | if(value == NULL) 691 | return(-1); 692 | 693 | /* Longueur */ 694 | length = strlen(line_data); 695 | } 696 | else if(line_data[0] == ' ' || line_data[0] == '\t') 697 | { 698 | /** Valeur vide **/ 699 | /* valeur */ 700 | value = strdup(""); 701 | if(value == NULL) 702 | return(-1); 703 | 704 | /* Longueur */ 705 | length = 0; 706 | while(line_data[length] == ' ' || line_data[length] == '\t') 707 | length++; 708 | } 709 | else 710 | { 711 | /** Valeur **/ 712 | for(i=0; i<(int) strlen(line_data); i++) 713 | { 714 | if(line_data[i] == '"') 715 | { 716 | end_sep = strchr(&line_data[i+1],'"'); 717 | if(end_sep == NULL) 718 | return(-2); 719 | i += ((end_sep - &line_data[i+1]) + 1); 720 | } 721 | else if(line_data[i] == '\'') 722 | { 723 | end_sep = strchr(&line_data[i+1],'\''); 724 | if(end_sep == NULL) 725 | return(-2); 726 | i += ((end_sep - &line_data[i+1]) + 1); 727 | } 728 | else if(line_data[i] == ' ' || line_data[i] == '\t') 729 | break; 730 | } 731 | length = i; 732 | 733 | /* Allocation mémoire */ 734 | value = calloc(1,length+1); 735 | if(value == NULL) 736 | return(-1); 737 | memcpy(value,line_data,length); 738 | 739 | /* Continue tant qu'il y a des espaces */ 740 | while(line_data[length] == ' ' || line_data[length] == '\t') 741 | length++; 742 | } 743 | 744 | /* Renvoi la valeur */ 745 | *value_rtn = value; 746 | return(length); 747 | } 748 | 749 | 750 | /****************************************************************/ 751 | /* mem_free_line() : Libération mémoire de la structure line. */ 752 | /****************************************************************/ 753 | static void mem_free_line(struct line *current_line) 754 | { 755 | if(current_line) 756 | { 757 | if(current_line->label) 758 | free(current_line->label); 759 | 760 | if(current_line->opcode) 761 | free(current_line->opcode); 762 | 763 | if(current_line->operand) 764 | free(current_line->operand); 765 | 766 | if(current_line->comment) 767 | free(current_line->comment); 768 | 769 | free(current_line); 770 | } 771 | } 772 | 773 | /***********************************************************************/ 774 | -------------------------------------------------------------------------------- /Src/Prodos_Source.h: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* Prodos_Source.h : Header pour la gestion des fichiers Source. */ 4 | /* */ 5 | /***********************************************************************/ 6 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Dec 2011 */ 7 | /***********************************************************************/ 8 | 9 | int ClearFileHighBit(char *); 10 | int SetFileHighBit(char *); 11 | int IndentFile(char *); 12 | int OutdentFile(char *); 13 | 14 | /***********************************************************************/ 15 | -------------------------------------------------------------------------------- /Src/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include 3 | #include 4 | 5 | static struct logconf LCF = { 6 | true, 7 | INFO 8 | }; 9 | 10 | int logf_impl(loglevel level, const char *fmt, ...) 11 | { 12 | if (!LCF.enabled || level > LCF.level) return 0; 13 | 14 | va_list varargs; 15 | va_start(varargs, fmt); 16 | 17 | return vprintf(fmt, varargs); 18 | } 19 | 20 | void log_off() 21 | { 22 | LCF.enabled = false; 23 | } 24 | 25 | void log_on() 26 | { 27 | LCF.enabled = true; 28 | } 29 | 30 | void log_set_level(loglevel level) 31 | { 32 | LCF.level = level; 33 | } -------------------------------------------------------------------------------- /Src/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic logger 3 | * Author: David Stancu, @mach-kernel, Apr. 2018 4 | */ 5 | 6 | #pragma once 7 | #include 8 | 9 | typedef enum loglevel { 10 | ALWAYS, 11 | ERROR, 12 | INFO 13 | } loglevel; 14 | 15 | typedef struct logconf { 16 | bool enabled; 17 | loglevel level; 18 | } logconf; 19 | 20 | int logf_impl(loglevel level, const char *fmt, ...); 21 | 22 | #define logf(fmt, ...) logf_impl(ALWAYS, fmt, ##__VA_ARGS__) 23 | #define logf_info(fmt, ...) logf_impl(INFO, fmt, ##__VA_ARGS__) 24 | #define logf_error(fmt, ...) logf_impl(ERROR, fmt, ##__VA_ARGS__) 25 | 26 | void log_off(); 27 | void log_on(); 28 | void log_set_level(loglevel level); -------------------------------------------------------------------------------- /Src/os/os.c: -------------------------------------------------------------------------------- 1 | /** 2 | * OS specific calls for file manipulations. Generic functionality 3 | * where API between POSIX and Win32 APIs overlap is to be placed here, 4 | * otherwise the correct support code will be loaded at build time. 5 | * 6 | * Authors: 7 | * Olivier Zardini, Brutal Deluxe Software, Mar. 2012 8 | * David Stancu, @mach-kernel, January 2018 9 | * 10 | */ 11 | 12 | #include "os.h" 13 | 14 | /** 15 | * Delete file at path 16 | * @brief os_DeleteFile 17 | * @param file_path 18 | */ 19 | void os_DeleteFile(char *file_path) 20 | { 21 | #ifdef BUILD_WINDOWS 22 | os_SetFileAttribute(file_path, SET_FILE_VISIBLE); 23 | #endif 24 | unlink(file_path); 25 | } 26 | 27 | /** 28 | * Recursively (if necessary) creates a directory. This should work 29 | * on both POSIX and the classic Win32 C runtime, but will not work 30 | * with UWP. 31 | * 32 | * @brief os_CreateDirectory Create a directory 33 | * @param directory char *directory 34 | * @return 35 | */ 36 | int os_CreateDirectory(char *directory) 37 | { 38 | int error = 0; 39 | struct stat dirstat; 40 | 41 | char *dir_tokenize = strdup(directory); 42 | 43 | char *buffer = calloc(1, 1024); 44 | if (dir_tokenize[0] == FOLDER_CHARACTER[0]) 45 | strcat(buffer, FOLDER_CHARACTER); 46 | char *token = strtok(dir_tokenize, FOLDER_CHARACTER); 47 | 48 | while (token) { 49 | if (strlen(buffer) + strlen(token) > 1024) return(-1); 50 | strcat(buffer, token); 51 | 52 | if (stat(buffer, &dirstat) != 0) 53 | error = my_mkdir(buffer); 54 | else if (!S_ISDIR(dirstat.st_mode)) 55 | error = my_mkdir(buffer); 56 | 57 | strcat(buffer, FOLDER_CHARACTER); 58 | token = strtok(NULL, FOLDER_CHARACTER); 59 | } 60 | 61 | return error; 62 | } 63 | -------------------------------------------------------------------------------- /Src/os/os.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /***********************************************************************/ 4 | /* */ 5 | /* Dc_OS.h : Header pour les fonctions spécifiques à l'OS. */ 6 | /* */ 7 | /***********************************************************************/ 8 | /* Auteur : Olivier ZARDINI * Brutal Deluxe Software * Mar 2012 */ 9 | /***********************************************************************/ 10 | 11 | #pragma once 12 | 13 | #if defined(_WIN32) || defined(_WIN64) 14 | #define BUILD_WINDOWS 1 15 | #else 16 | #define BUILD_POSIX 1 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef BUILD_POSIX 31 | 32 | #define FOLDER_CHARACTER "/" 33 | 34 | #include 35 | #include 36 | 37 | #endif 38 | 39 | #ifdef BUILD_WINDOWS 40 | 41 | #pragma warning(disable:4996) 42 | 43 | #define FOLDER_CHARACTER "\\" 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #endif 51 | 52 | 53 | #if !defined(S_ISDIR) && defined(S_IFDIR) 54 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 55 | #endif 56 | 57 | #define SET_FILE_VISIBLE 1 58 | #define SET_FILE_HIDDEN 2 59 | 60 | #include "../Dc_Shared.h" 61 | #include "../Dc_Prodos.h" 62 | #include "../Dc_Memory.h" 63 | 64 | int os_GetFolderFiles(char *,char *); 65 | int os_CreateDirectory(char *directory); 66 | void os_DeleteFile(char *file_path); 67 | void os_SetFileCreationModificationDate(char *,struct file_descriptive_entry *); 68 | void os_GetFileCreationModificationDate(char *,struct prodos_file *); 69 | void os_SetFileAttribute(char *,int); 70 | int my_stricmp(char *,char *); 71 | int my_strnicmp(char *,char *,size_t); 72 | int my_mkdir(char *path); 73 | 74 | char *my_strcpy(char *s1, int s1_size, char *s2); 75 | char *my_strdup(const char *s); 76 | 77 | uint32_t swap32(uint32_t num); 78 | uint16_t swap16(uint16_t num); 79 | 80 | /***********************************************************************/ 81 | -------------------------------------------------------------------------------- /Src/os/posix.c: -------------------------------------------------------------------------------- 1 | /** 2 | * POSIX runtime calls for file manipulations 3 | * 4 | * Author: David Stancu, @mach-kernel, Jan. 2018 5 | * 6 | */ 7 | 8 | #include "os.h" 9 | 10 | #ifdef BUILD_POSIX 11 | 12 | /** 13 | * Creates a directory. The POSIX compliant one requires a mask, 14 | * but the Win32 one does not. 15 | * 16 | * FWIW, the Win32 POSIX mkdir has been deprecated so we use _mkdir 17 | * from direct.h (https://msdn.microsoft.com/en-us/library/2fkk4dzw.aspx) 18 | * 19 | * @param char *path 20 | * @return 21 | */ 22 | int my_mkdir(char *path) 23 | { 24 | return mkdir(path, S_IRWXU); 25 | } 26 | 27 | /** 28 | * Delegator to strcasecmp 29 | * 30 | * @brief my_stricmp 31 | * @param string1 32 | * @param string2 33 | * @return 34 | */ 35 | int my_stricmp(char *string1, char *string2) 36 | { 37 | return(strcasecmp(string1,string2)); 38 | } 39 | 40 | /** 41 | * Delegator to strnicmp 42 | * 43 | * @brief my_strnicmp 44 | * @param string1 45 | * @param string2 46 | * @param length 47 | * @return 48 | */ 49 | int my_strnicmp(char *string1, char *string2, size_t length) 50 | { 51 | return(strncasecmp(string1,string2,length)); 52 | } 53 | 54 | 55 | /** 56 | * Appears to be invoked via char **BuildFileList(), which is then 57 | * used for things like the CLI set high bit or indent actions. 58 | * 59 | * Heap allocs the path and then inserts it into a singleton store 60 | * via my_Memory. 61 | * 62 | * @brief GetFolderFiles Query OS and load path structure into store 63 | * @param folder_path 64 | * @param hierarchy 65 | * @return 66 | */ 67 | int os_GetFolderFiles(char *folder_path, char *hierarchy) 68 | { 69 | struct dirent **entrylist; 70 | struct dirent *entry; 71 | int count, i; 72 | int error = 0; 73 | if (folder_path == NULL || strlen(folder_path) == 0) return(0); 74 | count = scandir(folder_path, &entrylist, NULL, alphasort); 75 | if (count < 0) return(1); 76 | for (i=0; id_name) + 2); 82 | strcpy(heap_path, folder_path); 83 | 84 | // If there's no trailing dir slash, we append it 85 | char last_char = heap_path[strlen(heap_path) - 1]; 86 | if (last_char != '/') strcat(heap_path, FOLDER_CHARACTER); 87 | 88 | // Append the filename to the path we copied earlier 89 | strcat(heap_path, entry->d_name); 90 | 91 | // POSIX only guarantees that you'll have d_name, 92 | // so we're going to use the S_ISREG macro which is more reliable 93 | struct stat dirent_stat; 94 | int staterr = stat(heap_path, &dirent_stat); 95 | if (staterr) return(staterr); 96 | 97 | if (S_ISREG(dirent_stat.st_mode)) { 98 | if (MatchHierarchie(heap_path, hierarchy)){ 99 | my_Memory(MEMORY_ADD_FILE, heap_path, NULL); 100 | } 101 | } 102 | else if (S_ISDIR(dirent_stat.st_mode)) { 103 | if (!my_stricmp(entry->d_name, ".") || !my_stricmp(entry->d_name, "..")) { 104 | free(heap_path); 105 | continue; 106 | } 107 | 108 | error = os_GetFolderFiles(heap_path, hierarchy); 109 | if (error) break; 110 | } 111 | 112 | free(heap_path); 113 | free(entry); 114 | } 115 | free(entrylist); 116 | 117 | return error; 118 | } 119 | 120 | /** 121 | * Annotate an inode with updated timestamp for created / modified 122 | * 123 | * @brief os_SetFileCreationModificationDate 124 | * @param path 125 | * @param entry 126 | */ 127 | void os_SetFileCreationModificationDate(char *path, struct file_descriptive_entry *entry) { 128 | struct stat filestat; 129 | if(stat(path, &filestat)) return; 130 | 131 | time_t curtime; 132 | time(&curtime); 133 | struct tm *time = localtime(&curtime); 134 | 135 | time->tm_mday = entry->file_creation_date.day; 136 | time->tm_mon = entry->file_creation_date.month; 137 | time->tm_year = entry->file_creation_date.year; 138 | time->tm_hour = entry->file_creation_time.hour; 139 | time->tm_min = entry->file_creation_time.minute; 140 | 141 | time_t modtime = mktime(time); 142 | 143 | struct utimbuf utb = { 144 | .actime = modtime, 145 | .modtime = modtime 146 | }; 147 | 148 | utime(path, &utb); 149 | } 150 | 151 | /** 152 | * Annotate the ProDOS file entry modify + create timestamps 153 | * to mirror the libc/stat timestamp. 154 | * 155 | * @brief os_GetFileCreationModificationDate 156 | * @param path 157 | * @param file 158 | */ 159 | void os_GetFileCreationModificationDate(char *path, struct prodos_file *file) { 160 | struct stat filestat; 161 | if (stat(path, &filestat)) return; 162 | 163 | // TODO: Apply TZ transformations? 164 | struct tm *time = localtime(&filestat.st_mtime); 165 | if (time == NULL) return; 166 | 167 | file->file_creation_date = BuildProdosDate(time->tm_mday, time->tm_mon + 1, time->tm_year + 1900); 168 | file->file_creation_time = BuildProdosTime(time->tm_min, time->tm_hour); 169 | file->file_modification_date = BuildProdosDate(time->tm_mday, time->tm_mon + 1, time->tm_year + 1900); 170 | file->file_modification_time = BuildProdosTime(time->tm_min, time->tm_hour); 171 | } 172 | 173 | 174 | char *my_strcpy(char *s1, int s1_size, char *s2) 175 | { 176 | return strcpy(s1, s2); 177 | } 178 | 179 | char *my_strdup(const char *s) 180 | { 181 | return strdup(s); 182 | } 183 | 184 | uint32_t swap32(uint32_t num) 185 | { 186 | return __builtin_bswap32(num); 187 | } 188 | 189 | uint16_t swap16(uint16_t num) 190 | { 191 | return __builtin_bswap16(num); 192 | } 193 | #endif 194 | -------------------------------------------------------------------------------- /Src/os/win32.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Win32 C runtime calls for file manipulations 3 | * 4 | * Author: Olivier Zardini, Brutal Deluxe Software, Mar. 2012 5 | * 6 | */ 7 | 8 | #include "os.h" 9 | 10 | #ifdef BUILD_WINDOWS 11 | 12 | /** 13 | * Win32 C runtime get contents of directory. 14 | * 15 | * @brief GetFolderFiles 16 | * @param folder_path 17 | * @param hierarchy 18 | * @return 19 | */ 20 | int os_GetFolderFiles(char *folder_path, char *hierarchy) 21 | { 22 | int error, rc; 23 | long hFile; 24 | int first_time = 1; 25 | struct _finddata_t c_file; 26 | char *buffer_folder_path = NULL; 27 | char *buffer_file_path = NULL; 28 | 29 | /* Rien à faire */ 30 | if(folder_path == NULL) 31 | return(0); 32 | if(strlen(folder_path) == 0) 33 | return(0); 34 | error = 0; 35 | 36 | /* Allocation mémoire */ 37 | buffer_folder_path = (char *) calloc(1,1024); 38 | buffer_file_path = (char *) calloc(1,1024); 39 | if(buffer_folder_path == NULL || buffer_file_path == NULL) 40 | return(1); 41 | strcpy(buffer_folder_path,folder_path); 42 | if(buffer_folder_path[strlen(buffer_folder_path)-1] != '\\' && buffer_folder_path[strlen(buffer_folder_path)-1] != '/') 43 | strcat(buffer_folder_path,FOLDER_CHARACTER); 44 | strcat(buffer_folder_path,"*.*"); 45 | 46 | /** On boucle sur tous les fichiers présents **/ 47 | while(1) 48 | { 49 | if(first_time == 1) 50 | { 51 | hFile = _findfirst(buffer_folder_path,&c_file); 52 | rc = (int) hFile; 53 | } 54 | else 55 | rc = _findnext(hFile,&c_file); 56 | 57 | /* On analyse le résultat */ 58 | if(rc == -1) 59 | break; /* no more files */ 60 | 61 | /** On traite cette entrée **/ 62 | first_time++; 63 | strcpy(buffer_file_path,folder_path); 64 | if(buffer_file_path[strlen(buffer_file_path)-1] != '\\' && buffer_file_path[strlen(buffer_file_path)-1] != '/') 65 | strcat(buffer_file_path,FOLDER_CHARACTER); 66 | strcat(buffer_file_path,c_file.name); 67 | 68 | /** Traite le dossier de façon récursive **/ 69 | if((c_file.attrib & _A_SUBDIR) == _A_SUBDIR) 70 | { 71 | /* On ne traite ni . ni .. */ 72 | if(!my_stricmp(c_file.name,".") || !my_stricmp(c_file.name,"..")) 73 | continue; 74 | 75 | /* Recherche dans le contenu du dossier */ 76 | error = os_GetFolderFiles(buffer_file_path,hierarchy); 77 | if(error) 78 | break; 79 | } 80 | else 81 | { 82 | /* Conserve le nom du fichier */ 83 | if(MatchHierarchie(buffer_file_path,hierarchy)) 84 | my_Memory(MEMORY_ADD_FILE,buffer_file_path,NULL); 85 | } 86 | } 87 | 88 | /* On ferme */ 89 | _findclose(hFile); 90 | 91 | /* Libération mémoire */ 92 | free(buffer_folder_path); 93 | free(buffer_file_path); 94 | 95 | return(error); 96 | } 97 | 98 | /** 99 | * Win32 C runtime get file modification date 100 | * 101 | * @brief my_GetFileCreationModificationDate 102 | * @param file_data_path 103 | * @param current_file 104 | */ 105 | void os_GetFileCreationModificationDate(char *file_data_path, struct prodos_file *current_file) 106 | { 107 | BOOL result; 108 | HANDLE fd; 109 | SYSTEMTIME system_utc; 110 | SYSTEMTIME system_date; 111 | FILETIME creation_date; 112 | FILETIME access_date; 113 | FILETIME modification_date; 114 | 115 | /* Init */ 116 | memset(&creation_date,0,sizeof(FILETIME)); 117 | memset(&modification_date,0,sizeof(FILETIME)); 118 | 119 | /* Récupère un Handle sur le fichier */ 120 | fd = CreateFile((LPCTSTR)file_data_path,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 121 | if(fd == INVALID_HANDLE_VALUE) 122 | return; 123 | 124 | /* Récupération des dates */ 125 | result = GetFileTime(fd,&creation_date,&access_date,&modification_date); 126 | 127 | /* Fermeture du fichier */ 128 | CloseHandle(fd); 129 | 130 | /** Création des dates au format Prodos **/ 131 | /* Création Date */ 132 | FileTimeToSystemTime(&creation_date,&system_utc); 133 | SystemTimeToTzSpecificLocalTime(NULL,&system_utc,&system_date); 134 | current_file->file_creation_date = BuildProdosDate(system_date.wDay,system_date.wMonth,system_date.wYear); 135 | current_file->file_creation_time = BuildProdosTime(system_date.wMinute,system_date.wHour); 136 | 137 | /* Modification Date */ 138 | FileTimeToSystemTime(&modification_date,&system_utc); 139 | SystemTimeToTzSpecificLocalTime(NULL,&system_utc,&system_date); 140 | current_file->file_modification_date = BuildProdosDate(system_date.wDay,system_date.wMonth,system_date.wYear); 141 | current_file->file_modification_time = BuildProdosTime(system_date.wMinute,system_date.wHour); 142 | } 143 | 144 | /** 145 | * Win32 C runtime set file creation / modification date 146 | * 147 | * @brief os_SetFileCreationModificationDate 148 | * @param file_data_path 149 | * @param current_entry 150 | */ 151 | void os_SetFileCreationModificationDate(char *file_data_path, struct file_descriptive_entry *current_entry) 152 | { 153 | BOOL result; 154 | HANDLE fd; 155 | SYSTEMTIME system_date; 156 | SYSTEMTIME system_utc; 157 | FILETIME creation_date; 158 | FILETIME modification_date; 159 | 160 | /* Init */ 161 | memset(&creation_date,0,sizeof(FILETIME)); 162 | memset(&modification_date,0,sizeof(FILETIME)); 163 | 164 | /* Récupère un Handle sur le fichier */ 165 | fd = CreateFile((LPCTSTR)file_data_path,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 166 | if(fd == INVALID_HANDLE_VALUE) 167 | return; 168 | 169 | /** Création des dates au format Windows **/ 170 | /* Création Date */ 171 | memset(&system_date,0,sizeof(SYSTEMTIME)); 172 | system_date.wYear = current_entry->file_creation_date.year + ((current_entry->file_creation_date.year < 70) ? 2000 : 1900); 173 | system_date.wMonth = current_entry->file_creation_date.month; 174 | system_date.wDay = current_entry->file_creation_date.day; 175 | system_date.wHour = current_entry->file_creation_time.hour; 176 | system_date.wMinute = current_entry->file_creation_time.minute; 177 | TzSpecificLocalTimeToSystemTime(NULL,&system_date,&system_utc); 178 | result = SystemTimeToFileTime(&system_utc,&creation_date); 179 | /* Modification Date */ 180 | memset(&system_date,0,sizeof(SYSTEMTIME)); 181 | system_date.wYear = current_entry->file_modification_date.year + ((current_entry->file_modification_date.year < 70) ? 2000 : 1900); 182 | system_date.wMonth = current_entry->file_modification_date.month; 183 | system_date.wDay = current_entry->file_modification_date.day; 184 | system_date.wHour = current_entry->file_modification_time.hour; 185 | system_date.wMinute = current_entry->file_modification_time.minute; 186 | TzSpecificLocalTimeToSystemTime(NULL,&system_date,&system_utc); 187 | result = SystemTimeToFileTime(&system_utc,&modification_date); 188 | 189 | /** Changement des Dates du fichier **/ 190 | result = SetFileTime(fd,&creation_date,(LPFILETIME)NULL,&modification_date); 191 | 192 | /* Fermeture du fichier */ 193 | CloseHandle(fd); 194 | } 195 | 196 | /** 197 | * Only applies to the Win32 API, you could ostensibly do this 198 | * by invoking rename() to make it a dotfile but that is silly. 199 | * 200 | * @brief my_SetFileAttribute Win32 toggle hidden file header for path 201 | * @param file_path Path 202 | * @param flag Either SET_FILE_VISIBLE or SET_FILE_HIDDEN 203 | */ 204 | void os_SetFileAttribute(char *file_path, int flag) 205 | { 206 | DWORD file_attributes; 207 | 208 | /* Attributs du fichier */ 209 | file_attributes = GetFileAttributes(file_path); 210 | 211 | /* Change la visibilité */ 212 | if(flag == SET_FILE_VISIBLE) 213 | { 214 | /* Montre le fichier */ 215 | if((file_attributes | FILE_ATTRIBUTE_HIDDEN) == file_attributes) 216 | SetFileAttributes(file_path,file_attributes - FILE_ATTRIBUTE_HIDDEN); 217 | } 218 | else if(flag == SET_FILE_HIDDEN) 219 | { 220 | /* Cache le fichier */ 221 | if((file_attributes | FILE_ATTRIBUTE_HIDDEN) != file_attributes) 222 | SetFileAttributes(file_path,file_attributes | FILE_ATTRIBUTE_HIDDEN); 223 | } 224 | } 225 | 226 | /** 227 | * Win32 C runtime case insensitive string comparison 228 | * @brief mystricmp 229 | * @param string1 230 | * @param string2 231 | * @return 232 | */ 233 | int my_stricmp(char *string1, char *string2) 234 | { 235 | return(stricmp(string1, string2)); 236 | } 237 | 238 | /** 239 | * Win32 C runtime case insensitive bounded string compare 240 | * 241 | * @brief my_strnicmp 242 | * @param string1 243 | * @param string2 244 | * @param length 245 | * @return 246 | */ 247 | int my_strnicmp(char *string1, char *string2, size_t length) 248 | { 249 | return strnicmp(string1, string2, length); 250 | } 251 | 252 | char *my_strcpy(char *s1, int s1_size, char *s2) 253 | { 254 | return strcpy_s(s1, s1_size, s2); 255 | } 256 | 257 | char *my_strdup(char *s) 258 | { 259 | return _strdup(s); 260 | } 261 | 262 | int my_mkdir(char *path) 263 | { 264 | return mkdir(path); 265 | } 266 | 267 | uint32_t swap32(uint32_t num) 268 | { 269 | return _byteswap_ulong(num); 270 | } 271 | 272 | uint16_t swap16(uint16_t num) 273 | { 274 | return _byteswap_ushort(num); 275 | } 276 | 277 | #endif 278 | -------------------------------------------------------------------------------- /cadius.pro: -------------------------------------------------------------------------------- 1 | # Created by and for Qt Creator This file was created for editing the project sources only. 2 | # You may attempt to use it for building too, by modifying this file here. 3 | 4 | #TARGET = cadius 5 | 6 | CONFIG -= qt 7 | 8 | mac { 9 | CONFIG -= app_bundle 10 | } 11 | 12 | HEADERS = \ 13 | $$PWD/Src/Dc_Memory.h \ 14 | $$PWD/Src/Dc_Prodos.h \ 15 | $$PWD/Src/Dc_Shared.h \ 16 | $$PWD/Src/Prodos_Add.h \ 17 | $$PWD/Src/Prodos_Check.h \ 18 | $$PWD/Src/Prodos_Create.h \ 19 | $$PWD/Src/Prodos_Delete.h \ 20 | $$PWD/Src/Prodos_Dump.h \ 21 | $$PWD/Src/Prodos_Extract.h \ 22 | $$PWD/Src/Prodos_Move.h \ 23 | $$PWD/Src/Prodos_Rename.h \ 24 | $$PWD/Src/Prodos_Source.h \ 25 | $$PWD/Src/os/os.h 26 | 27 | SOURCES = \ 28 | $$PWD/Src/Dc_Memory.c \ 29 | $$PWD/Src/Dc_Prodos.c \ 30 | $$PWD/Src/Dc_Shared.c \ 31 | $$PWD/Src/Main.c \ 32 | $$PWD/Src/Prodos_Add.c \ 33 | $$PWD/Src/Prodos_Check.c \ 34 | $$PWD/Src/Prodos_Create.c \ 35 | $$PWD/Src/Prodos_Delete.c \ 36 | $$PWD/Src/Prodos_Dump.c \ 37 | $$PWD/Src/Prodos_Extract.c \ 38 | $$PWD/Src/Prodos_Move.c \ 39 | $$PWD/Src/Prodos_Rename.c \ 40 | $$PWD/Src/Prodos_Source.c \ 41 | $$PWD/Src/os/os.c \ 42 | $$PWD/Src/os/win32.c \ 43 | $$PWD/Src/os/posix.c 44 | 45 | INCLUDEPATH = \ 46 | $$PWD/Src 47 | 48 | #DEFINES = 49 | 50 | --------------------------------------------------------------------------------