Compare commits
27 Commits
1.2.3
...
seriesYear
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50c53e14da | ||
|
|
2e01a4db14 | ||
|
|
444e67100c | ||
|
|
82d054fd05 | ||
|
|
f82c024f8d | ||
|
|
da4daa6a8a | ||
|
|
6e1e8959c9 | ||
|
|
aedc5bedb4 | ||
|
|
93f5061c8f | ||
|
|
d46e171bd6 | ||
|
|
e7fe520660 | ||
|
|
91f288e8f4 | ||
|
|
d7bd3bb94b | ||
|
|
9e0b0ac01c | ||
|
|
03a8d906ea | ||
|
|
fff28cf6ae | ||
|
|
9ee95b8d5e | ||
|
|
11bf5a9709 | ||
|
|
af4b3af14e | ||
|
|
9bb7fbbc9e | ||
|
|
f877d620af | ||
|
|
c175e46b15 | ||
|
|
f0bc669d40 | ||
|
|
db3db48e5c | ||
|
|
cec585f8e0 | ||
|
|
d71a48d8d4 | ||
|
|
9e4a560911 |
63
.github/workflows/build.yaml
vendored
Normal file
63
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+*"
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.9]
|
||||
os: [ubuntu-latest, macos-10.15, windows-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python3 -m pip install -r requirements_dev.txt
|
||||
python3 -m pip install -r requirements.txt
|
||||
for requirement in requirements-*.txt; do
|
||||
python3 -m pip install -r "$requirement"
|
||||
done
|
||||
shell: bash
|
||||
- name: Install Windows dependencies
|
||||
run: |
|
||||
choco install -y zip
|
||||
if: runner.os == 'Windows'
|
||||
- name: build
|
||||
run: |
|
||||
make pydist
|
||||
make dist
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
if: runner.os != 'Linux' # linux binary currently has a segfault when running on latest fedora
|
||||
with:
|
||||
name: "${{ format('ComicTagger-{0}', runner.os) }}"
|
||||
path: dist/*.zip
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
prerelease: "${{ contains(github.ref, '-') }}" # alpha-releases should be 1.3.0-alpha.x full releases should be 1.3.0
|
||||
draft: true
|
||||
files: dist/*.zip
|
||||
- name: "Publish distribution 📦 to PyPI"
|
||||
if: startsWith(github.ref, 'refs/tags/') && runner.os == 'Linux'
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: piprelease
|
||||
167
.gitignore
vendored
167
.gitignore
vendored
@@ -1,12 +1,157 @@
|
||||
/.idea/
|
||||
/nbproject/
|
||||
/dist
|
||||
*.pyc
|
||||
/.vscode
|
||||
venv
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
build/
|
||||
# generated by setuptools_scm
|
||||
ctversion.py
|
||||
.eggs
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
|
||||
### Other editors
|
||||
.*.swp
|
||||
nbproject/
|
||||
.vscode
|
||||
|
||||
comictaggerlib/_version.py
|
||||
*.exe
|
||||
*.zip
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
65
.travis.yml
65
.travis.yml
@@ -1,42 +1,59 @@
|
||||
language: python
|
||||
# Only build tags
|
||||
if: type = pull_request OR tag IS present
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- /^\d+\.\d+\.\d+$/
|
||||
- /^\d+\.\d+\.\d+.*$/
|
||||
env:
|
||||
global:
|
||||
- PYTHON=python
|
||||
- PIP=pip
|
||||
- PYTHON=python3
|
||||
- PIP=pip3
|
||||
- SETUPTOOLS_SCM_PRETEND_VERSION=$TRAVIS_TAG
|
||||
- MAKE=make
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
- os: osx
|
||||
language: generic
|
||||
osx_image: xcode8.3
|
||||
env: PYTHON=python3 PIP=pip3 MACOSX_DEPLOYMENT_TARGET=10.11
|
||||
python: 3.8
|
||||
- name: "Python: 3.7"
|
||||
os: osx
|
||||
language: shell
|
||||
python: 3.7
|
||||
env: PYTHON=python3 PIP="python3 -m pip"
|
||||
cache:
|
||||
- directories:
|
||||
- $HOME/Library/Caches/pip
|
||||
- os: windows
|
||||
language: bash
|
||||
env: PATH=/C/Python37:/C/Python37/Scripts:$PATH MAKE=mingw32-make
|
||||
env: PATH=/C/Python37:/C/Python37/Scripts:$PATH MAKE=mingw32-make PIP=pip PYTHON=python
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install -y python mingw zip; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew upgrade python3 ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install -y python --version 3.7.9; choco install -y mingw zip; fi
|
||||
install:
|
||||
- $PIP install --upgrade setuptools
|
||||
- $PIP install -r requirements.txt
|
||||
- $PIP install -r requirements_dev.txt
|
||||
- $PIP install -r requirements-GUI.txt
|
||||
- $PIP install -r requirements-CBR.txt
|
||||
script:
|
||||
- $MAKE dist
|
||||
- if [ "$TRAVIS_OS_NAME" != "linux" ]; then $MAKE dist ; fi
|
||||
|
||||
deploy:
|
||||
name: $TRAVIS_TAG
|
||||
body: Released ComicTagger $TRAVIS_TAG
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
api_key:
|
||||
secure: RgohcOJOfLhXXT12bMWaLwOqhe+ClSCYXjYuUJuWK4/E1fdd1xu1ebdQU+MI/R8cZ0Efz3sr2n3NkO/Aa8gN68xEfuF7RVRMm64P9oPrfZgGdsD6H43rU/6kN8bgaDRmCYpLTfXaJ+/gq0x1QDkhWJuceF2BYEGGvL0BvS/TUsLyjVxs8ujTplLyguXHNEv4/7Yz7SBNZZmUHjBuq/y+l8ds3ra9rSgAVAN1tMXoFKJPv+SNNkpTo5WUNMPzBnN041F1rzqHwYDLog2V7Krp9JkXzheRFdAr51/tJBYzEd8AtYVdYvaIvoO6A4PiTZ7MpsmcZZPAWqLQU00UTm/PhT/LVR+7+f8lOBG07RgNNHB+edjDRz3TAuqyuZl9wURWTZKTPuO49TkZMz7Wm0DRNZHvBm1IXLeSG7Tll2YL1+WpZNZg+Dhro2J1QD3vxDXafhMdTCB4z0q5aKpG93IT0p6oXOO0oEGOPZYbA2c5R3SXWSyqd1E1gdhbVjIZr59h++TEf1zz07tvWHqPuAF/Ly/j+dIcY2wj0EzRWaSASWgUpTnMljAkHtWhqDw4GXGDRkRUWRJl1d0/JyVqCeIdRzDQNl8/q7BcO3F1zqr1PgnYdz0lfwWxL1/ekw2vHOJE/GOdkyvX0aJrnaOV338mjJbfGHYv4ESc9ow1kdtIbiU=
|
||||
file_glob: true
|
||||
file: dist/*.zip
|
||||
draft: true
|
||||
on:
|
||||
tags: true
|
||||
- name: "$TRAVIS_TAG"
|
||||
body: Released ComicTagger $TRAVIS_TAG
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
api_key:
|
||||
secure: RgohcOJOfLhXXT12bMWaLwOqhe+ClSCYXjYuUJuWK4/E1fdd1xu1ebdQU+MI/R8cZ0Efz3sr2n3NkO/Aa8gN68xEfuF7RVRMm64P9oPrfZgGdsD6H43rU/6kN8bgaDRmCYpLTfXaJ+/gq0x1QDkhWJuceF2BYEGGvL0BvS/TUsLyjVxs8ujTplLyguXHNEv4/7Yz7SBNZZmUHjBuq/y+l8ds3ra9rSgAVAN1tMXoFKJPv+SNNkpTo5WUNMPzBnN041F1rzqHwYDLog2V7Krp9JkXzheRFdAr51/tJBYzEd8AtYVdYvaIvoO6A4PiTZ7MpsmcZZPAWqLQU00UTm/PhT/LVR+7+f8lOBG07RgNNHB+edjDRz3TAuqyuZl9wURWTZKTPuO49TkZMz7Wm0DRNZHvBm1IXLeSG7Tll2YL1+WpZNZg+Dhro2J1QD3vxDXafhMdTCB4z0q5aKpG93IT0p6oXOO0oEGOPZYbA2c5R3SXWSyqd1E1gdhbVjIZr59h++TEf1zz07tvWHqPuAF/Ly/j+dIcY2wj0EzRWaSASWgUpTnMljAkHtWhqDw4GXGDRkRUWRJl1d0/JyVqCeIdRzDQNl8/q7BcO3F1zqr1PgnYdz0lfwWxL1/ekw2vHOJE/GOdkyvX0aJrnaOV338mjJbfGHYv4ESc9ow1kdtIbiU=
|
||||
file_glob: true
|
||||
file: dist/*.zip
|
||||
draft: true
|
||||
on:
|
||||
tags: true
|
||||
condition: $TRAVIS_OS_NAME != "linux"
|
||||
- provider: pypi
|
||||
user: __token__
|
||||
password:
|
||||
secure: h+y5WkE8igf864dnsbGPFvOBkyPkuBYtnDRt+EgxHd71EZnV2YP7ns2Cx12su/SVVDdZCBlmHVtkhl6Jmqy+0rTkSYx+3mlBOqyl8Cj5+BlP/dP7Bdmhs2uLZk2YYL1avbC0A6eoNJFtCkjurnB/jCGE433rvMECWJ5x2HsQTKchCmDAEdAZbRBJrzLFsrIC+6NXW1IJZjd+OojbhLSyVar2Jr32foh6huTcBu/x278V1+zIC/Rwy3W67+3c4aZxYrI47FoYFza0jjFfr3EoSkKYUSByMTIvhWaqB2gIsF0T160jgDd8Lcgej+86ACEuG0v01VE7xoougqlOaJ94eAmapeM7oQXzekSwSAxcK3JQSfgWk/AvPhp07T4pQ8vCZmky6yqvVp1EzfKarTeub1rOnv+qo1znKLrBtOoq6t8pOAeczDdIDs51XT/hxaijpMRCM8vHxN4Kqnc4DY+3KcF7UFyH1ifQJHQe71tLBsM/GnAcJM5/3ykFVGvRJ716p4aa6IoGsdNk6bqlysNh7nURDl+bfm+CDXRkO2jkFwUFNqPHW7JwY6ZFx+b5SM3TzC3obJhfMS7OC37fo2geISOTR0xVie6NvpN6TjNAxFTfDxWJI7yH3Al2w43B3uYDd97WeiN+B+HVWtdaER87IVSRbRqFrRub+V+xrozT0y0=
|
||||
skip_existing: true
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
condition: $TRAVIS_OS_NAME = "linux"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include README.md
|
||||
include release_notes.txt
|
||||
include requirements.txt
|
||||
include unrar/*
|
||||
recursive-include scripts *.py *.txt
|
||||
recursive-include desktop-integration *
|
||||
include windows/app.ico
|
||||
|
||||
31
Makefile
31
Makefile
@@ -1,5 +1,6 @@
|
||||
PIP ?= pip
|
||||
VERSION_STR := $(shell python setup.py --version)
|
||||
PIP ?= pip3
|
||||
PYTHON ?= python3
|
||||
VERSION_STR := $(shell $(PYTHON) setup.py --version)
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OS_VERSION=win-$(PROCESSOR_ARCHITECTURE)
|
||||
@@ -14,7 +15,7 @@ else
|
||||
FINAL_NAME=ComicTagger-$(VERSION_STR)
|
||||
endif
|
||||
|
||||
.PHONY: all clean pydist upload unrar dist
|
||||
.PHONY: all clean pydist upload dist
|
||||
|
||||
all: clean dist
|
||||
|
||||
@@ -27,34 +28,22 @@ clean:
|
||||
rm -rf logdict*.log
|
||||
$(MAKE) -C mac clean
|
||||
rm -rf build
|
||||
$(MAKE) -C unrar clean
|
||||
rm -f unrar/libunrar.so unrar/libunrar.a unrar/unrar
|
||||
rm -f comictaggerlib/libunrar.so
|
||||
rm -rf comictaggerlib/ui/__pycache__
|
||||
rm comitaggerlib/ctversion.py
|
||||
rm comictaggerlib/ctversion.py
|
||||
|
||||
pydist:
|
||||
make clean
|
||||
mkdir -p piprelease
|
||||
rm -f comictagger-$(VERSION_STR).zip
|
||||
python setup.py sdist --formats=zip #,gztar
|
||||
mv dist/comictagger-$(VERSION_STR).zip piprelease
|
||||
$(PYTHON) setup.py sdist --formats=gztar
|
||||
mv dist/comictagger-$(VERSION_STR).tar.gz piprelease
|
||||
rm -rf comictagger.egg-info dist
|
||||
|
||||
upload:
|
||||
python setup.py register
|
||||
python setup.py sdist --formats=zip upload
|
||||
$(PYTHON) setup.py register
|
||||
$(PYTHON) setup.py sdist --formats=gztar upload
|
||||
|
||||
unrar:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# statically compile mingw dependencies
|
||||
# https://stackoverflow.com/questions/18138635/mingw-exe-requires-a-few-gcc-dlls-regardless-of-the-code
|
||||
$(MAKE) -C unrar LDFLAGS='-Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive -pthread -static-libgcc -static-libstdc++' lib
|
||||
else
|
||||
$(MAKE) -C unrar lib
|
||||
endif
|
||||
|
||||
dist: unrar
|
||||
dist:
|
||||
$(PIP) install .
|
||||
pyinstaller -y comictagger.spec
|
||||
cd dist && zip -r $(FINAL_NAME).zip $(APP_NAME)
|
||||
|
||||
14
README.md
14
README.md
@@ -8,7 +8,7 @@
|
||||
|
||||
ComicTagger is a **multi-platform** app for **writing metadata to digital comics**, written in Python and PyQt.
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
@@ -37,12 +37,14 @@ Just unzip the archive in any folder and run, no additional installation steps a
|
||||
A pip package is provided, you can install it with:
|
||||
|
||||
```
|
||||
$ pip install comictagger
|
||||
$ pip3 install comictagger[GUI]
|
||||
```
|
||||
|
||||
### From source
|
||||
|
||||
1. ensure you have a recent version of python3 and setuptools installed
|
||||
2. clone this repository `git clone https://github.com/comictagger/comictagger.git`
|
||||
3. `pip install -r requirements.txt`
|
||||
4. `python comictagger.py`
|
||||
1. Ensure you have a recent version of python3 installed
|
||||
2. Clone this repository `git clone https://github.com/comictagger/comictagger.git`
|
||||
3. `pip3 install -r requirements_dev.txt`
|
||||
4. Optionally install the GUI `pip3 install -r requirements-GUI.txt`
|
||||
5. Optionally install CBR support `pip3 install -r requirements-CBR.txt`
|
||||
6. `python3 comictagger.py`
|
||||
|
||||
@@ -21,58 +21,16 @@ import sys
|
||||
import tempfile
|
||||
import subprocess
|
||||
import platform
|
||||
import ctypes
|
||||
import time
|
||||
import io
|
||||
#import io
|
||||
#import locale
|
||||
#import shutil
|
||||
|
||||
from natsort import natsorted
|
||||
import natsort
|
||||
from PyPDF2 import PdfFileReader
|
||||
try:
|
||||
from unrar import rarfile
|
||||
from unrar import unrarlib
|
||||
from unrar import constants
|
||||
# monkey patch unrarlib to avoid segfaults on Win10
|
||||
if platform.system() == 'Windows':
|
||||
unrarlib.UNRARCALLBACK = ctypes.WINFUNCTYPE(
|
||||
# return type
|
||||
ctypes.c_int,
|
||||
# msg
|
||||
ctypes.c_uint,
|
||||
# UserData
|
||||
ctypes.c_long,
|
||||
# MONKEY PATCH HERE -- use a pointer instead of a long, in unrar code: (LPARAM)(*byte),
|
||||
# that is a pointer to byte casted to LPARAM
|
||||
# On win10 64bit causes nasty segfaults when used from pyinstaller
|
||||
ctypes.POINTER(ctypes.c_byte),
|
||||
# size
|
||||
ctypes.c_long
|
||||
)
|
||||
RARSetCallback = unrarlib._c_func(unrarlib.RARSetCallback, None,
|
||||
[unrarlib.HANDLE, unrarlib.UNRARCALLBACK, ctypes.c_long])
|
||||
def _rar_cb(self, msg, user_data, p1, p2):
|
||||
if (msg == constants.UCM_NEEDPASSWORD or
|
||||
msg == constants.UCM_NEEDPASSWORDW):
|
||||
# This is a work around since libunrar doesn't
|
||||
# properly return the error code when files are encrypted
|
||||
self._missing_password = True
|
||||
elif msg == constants.UCM_PROCESSDATA:
|
||||
if self._data is None:
|
||||
self._data = b''
|
||||
chunk = ctypes.string_at(p1, p2)
|
||||
self._data += chunk
|
||||
return 1
|
||||
rarfile._ReadIntoMemory._callback = _rar_cb
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("WARNING: cannot find libunrar, rar support is disabled")
|
||||
from unrar.cffi import rarfile
|
||||
except:
|
||||
pass
|
||||
|
||||
#if platform.system() == "Windows":
|
||||
# import _subprocess
|
||||
|
||||
try:
|
||||
import Image
|
||||
pil_available = True
|
||||
@@ -84,8 +42,6 @@ from .comicbookinfo import ComicBookInfo
|
||||
from .comet import CoMet
|
||||
from .genericmetadata import GenericMetadata, PageType
|
||||
from .filenameparser import FileNameParser
|
||||
#from settings import ComicTaggerSettings
|
||||
|
||||
|
||||
sys.path.insert(0, os.path.abspath("."))
|
||||
|
||||
@@ -95,7 +51,6 @@ class MetaDataStyle:
|
||||
COMET = 2
|
||||
name = ['ComicBookLover', 'ComicRack', 'CoMet']
|
||||
|
||||
|
||||
class ZipArchiver:
|
||||
|
||||
"""ZIP implementation"""
|
||||
@@ -177,11 +132,6 @@ class ZipArchiver:
|
||||
|
||||
This recompresses the zip archive, without the files in the exclude_list
|
||||
"""
|
||||
|
||||
# print ">> sys.stderr, Rebuilding zip {0} without {1}".format(
|
||||
# self.path, exclude_list )
|
||||
|
||||
# generate temp file
|
||||
tmp_fd, tmp_name = tempfile.mkstemp(dir=os.path.dirname(self.path))
|
||||
os.close(tmp_fd)
|
||||
|
||||
@@ -269,7 +219,6 @@ class ZipArchiver:
|
||||
|
||||
def copyFromArchive(self, otherArchive):
|
||||
"""Replace the current zip with one copied from another archive"""
|
||||
|
||||
try:
|
||||
zout = zipfile.ZipFile(self.path, 'w', allowZip64=True)
|
||||
for fname in otherArchive.getArchiveFilenameList():
|
||||
@@ -290,13 +239,8 @@ class ZipArchiver:
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
#------------------------------------------
|
||||
|
||||
class RarArchiver:
|
||||
|
||||
"""RAR implementation"""
|
||||
|
||||
devnull = None
|
||||
|
||||
def __init__(self, path, rar_exe_path):
|
||||
@@ -313,10 +257,6 @@ class RarArchiver:
|
||||
else:
|
||||
self.startupinfo = None
|
||||
|
||||
def __del__(self):
|
||||
# RarArchiver.devnull.close()
|
||||
pass
|
||||
|
||||
def getArchiveComment(self):
|
||||
rarc = self.getRARObj()
|
||||
return rarc.comment
|
||||
@@ -357,10 +297,6 @@ class RarArchiver:
|
||||
return False
|
||||
|
||||
def readArchiveFile(self, archive_file):
|
||||
|
||||
# Make sure to escape brackets, since some funky stuff is going on
|
||||
# underneath with "fnmatch"
|
||||
#archive_file = archive_file.replace("[", '[[]')
|
||||
entries = []
|
||||
|
||||
rarc = self.getRARObj()
|
||||
@@ -369,23 +305,14 @@ class RarArchiver:
|
||||
while tries < 7:
|
||||
try:
|
||||
tries = tries + 1
|
||||
#tmp_folder = tempfile.mkdtemp()
|
||||
#tmp_file = os.path.join(tmp_folder, archive_file)
|
||||
#rarc.extract(archive_file, tmp_folder)
|
||||
data = rarc.open(archive_file).read()
|
||||
#data = open(tmp_file).read()
|
||||
data = rarc.open(archive_file).read()
|
||||
entries = [(rarc.getinfo(archive_file), data)]
|
||||
|
||||
#shutil.rmtree(tmp_folder, ignore_errors=True)
|
||||
|
||||
#entries = rarc.read_files( archive_file )
|
||||
|
||||
if entries[0][0].file_size != len(entries[0][1]):
|
||||
print("readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
|
||||
entries[0][0].file_size, len(
|
||||
entries[0][1]), self.path, archive_file, tries), file=sys.stderr)
|
||||
continue
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print("readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(
|
||||
str(e), self.path, archive_file, tries), file=sys.stderr)
|
||||
@@ -472,16 +399,11 @@ class RarArchiver:
|
||||
return False
|
||||
|
||||
def getArchiveFilenameList(self):
|
||||
|
||||
rarc = self.getRARObj()
|
||||
#namelist = [ item.filename for item in rarc.infolist() ]
|
||||
# return namelist
|
||||
|
||||
tries = 0
|
||||
while tries < 7:
|
||||
try:
|
||||
tries = tries + 1
|
||||
#namelist = [ item.filename for item in rarc.infolist() ]
|
||||
namelist = []
|
||||
for item in rarc.infolist():
|
||||
if item.file_size != 0:
|
||||
@@ -503,7 +425,7 @@ class RarArchiver:
|
||||
while tries < 7:
|
||||
try:
|
||||
tries = tries + 1
|
||||
rarc = rarfile.RarFile( self.path )
|
||||
rarc = rarfile.RarFile(self.path)
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print("getRARObj(): [{0}] {1} attempt#{2}".format(
|
||||
@@ -606,7 +528,6 @@ class UnknownArchiver:
|
||||
def getArchiveFilenameList(self):
|
||||
return []
|
||||
|
||||
|
||||
class PdfArchiver:
|
||||
|
||||
def __init__(self, path):
|
||||
@@ -635,13 +556,8 @@ class PdfArchiver:
|
||||
out.append("/%04d.jpg" % (page))
|
||||
return out
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
|
||||
class ComicArchive:
|
||||
|
||||
logo_data = None
|
||||
|
||||
class ArchiveType:
|
||||
Zip, Rar, Folder, Pdf, Unknown = list(range(5))
|
||||
|
||||
@@ -716,11 +632,9 @@ class ComicArchive:
|
||||
|
||||
def rarTest(self):
|
||||
try:
|
||||
rarc = rarfile.RarFile(self.path)
|
||||
except: # InvalidRARArchive:
|
||||
return rarfile.is_rarfile(self.path)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def isZip(self):
|
||||
return self.archive_type == self.ArchiveType.Zip
|
||||
@@ -758,7 +672,6 @@ class ComicArchive:
|
||||
return self.isWritable()
|
||||
|
||||
def seemsToBeAComicArchive(self):
|
||||
|
||||
# Do we even care about extensions??
|
||||
ext = os.path.splitext(self.path)[1].lower()
|
||||
|
||||
@@ -785,7 +698,6 @@ class ComicArchive:
|
||||
return GenericMetadata()
|
||||
|
||||
def writeMetadata(self, metadata, style):
|
||||
|
||||
retcode = None
|
||||
if style == MetaDataStyle.CIX:
|
||||
retcode = self.writeCIX(metadata)
|
||||
@@ -796,7 +708,6 @@ class ComicArchive:
|
||||
return retcode
|
||||
|
||||
def hasMetadata(self, style):
|
||||
|
||||
if style == MetaDataStyle.CIX:
|
||||
return self.hasCIX()
|
||||
elif style == MetaDataStyle.CBI:
|
||||
@@ -817,7 +728,6 @@ class ComicArchive:
|
||||
return retcode
|
||||
|
||||
def getPage(self, index):
|
||||
|
||||
image_data = None
|
||||
|
||||
filename = self.getPageName(index)
|
||||
@@ -832,7 +742,6 @@ class ComicArchive:
|
||||
return image_data
|
||||
|
||||
def getPageName(self, index):
|
||||
|
||||
if index is None:
|
||||
return None
|
||||
|
||||
@@ -845,7 +754,6 @@ class ComicArchive:
|
||||
return page_list[index]
|
||||
|
||||
def getScannerPageIndex(self):
|
||||
|
||||
scanner_page_index = None
|
||||
|
||||
# make a guess at the scanner page
|
||||
@@ -900,7 +808,6 @@ class ComicArchive:
|
||||
return scanner_page_index
|
||||
|
||||
def getPageNameList(self, sort_list=True):
|
||||
|
||||
if self.page_list is None:
|
||||
# get the list file names in the archive, and sort
|
||||
files = self.archiver.getArchiveFilenameList()
|
||||
@@ -909,13 +816,9 @@ class ComicArchive:
|
||||
# about case-sensitivity!
|
||||
if sort_list:
|
||||
def keyfunc(k):
|
||||
# hack to account for some weird scanner ID pages
|
||||
# basename=os.path.split(k)[1]
|
||||
# if basename < '0':
|
||||
# k = os.path.join(os.path.split(k)[0], "z" + basename)
|
||||
return k.lower()
|
||||
|
||||
files = natsorted(files, key=keyfunc, signed=False)
|
||||
files = natsort.natsorted(files, alg=natsort.ns.IC | natsort.ns.I)
|
||||
|
||||
# make a sub-list of image files
|
||||
self.page_list = []
|
||||
@@ -930,7 +833,6 @@ class ComicArchive:
|
||||
return self.page_list
|
||||
|
||||
def getNumberOfPages(self):
|
||||
|
||||
if self.page_count is None:
|
||||
self.page_count = len(self.getPageNameList())
|
||||
return self.page_count
|
||||
@@ -1020,7 +922,6 @@ class ComicArchive:
|
||||
return raw_cix
|
||||
|
||||
def writeCIX(self, metadata):
|
||||
|
||||
if metadata is not None:
|
||||
self.applyArchiveInfoToMetadata(metadata, calc_page_sizes=True)
|
||||
cix_string = ComicInfoXml().stringFromMetadata(metadata)
|
||||
@@ -1180,7 +1081,6 @@ class ComicArchive:
|
||||
p['ImageSize'] = str(len(data))
|
||||
|
||||
def metadataFromFilename(self, parse_scan_info=True):
|
||||
|
||||
metadata = GenericMetadata()
|
||||
|
||||
fnp = FileNameParser()
|
||||
|
||||
@@ -24,39 +24,34 @@ from . import utils
|
||||
|
||||
|
||||
class ComicBookInfo:
|
||||
|
||||
def metadataFromString(self, string):
|
||||
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return None
|
||||
cbi_container = json.loads(str(string, 'utf-8'))
|
||||
|
||||
metadata = GenericMetadata()
|
||||
|
||||
cbi = cbi_container['ComicBookInfo/1.0']
|
||||
cbi = Default(cbi_container['ComicBookInfo/1.0'])
|
||||
|
||||
# helper func
|
||||
# If item is not in CBI, return None
|
||||
def xlate(cbi_entry):
|
||||
if cbi_entry in cbi:
|
||||
return cbi[cbi_entry]
|
||||
else:
|
||||
return None
|
||||
metadata.series = utils.xlate(cbi['series'])
|
||||
metadata.title = utils.xlate(cbi['title'])
|
||||
metadata.issue = utils.xlate(cbi['issue'])
|
||||
metadata.publisher = utils.xlate(cbi['publisher'])
|
||||
metadata.month = utils.xlate(cbi['publicationMonth'], True)
|
||||
metadata.year = utils.xlate(cbi['publicationYear'], True)
|
||||
metadata.seriesYear = utils.xlate('seriesPublicationYear', True)
|
||||
metadata.issueCount = utils.xlate(cbi['numberOfIssues'], True)
|
||||
metadata.comments = utils.xlate(cbi['comments'])
|
||||
metadata.genre = utils.xlate(cbi['genre'])
|
||||
metadata.volume = utils.xlate(cbi['volume'], True)
|
||||
metadata.volumeCount = utils.xlate(cbi['numberOfVolumes'], True)
|
||||
metadata.language = utils.xlate(cbi['language'])
|
||||
metadata.country = utils.xlate(cbi['country'])
|
||||
metadata.criticalRating = utils.xlate(cbi['rating'])
|
||||
|
||||
metadata.series = xlate('series')
|
||||
metadata.title = xlate('title')
|
||||
metadata.issue = xlate('issue')
|
||||
metadata.publisher = xlate('publisher')
|
||||
metadata.month = xlate('publicationMonth')
|
||||
metadata.year = xlate('publicationYear')
|
||||
metadata.issueCount = xlate('numberOfIssues')
|
||||
metadata.comments = xlate('comments')
|
||||
metadata.credits = xlate('credits')
|
||||
metadata.genre = xlate('genre')
|
||||
metadata.volume = xlate('volume')
|
||||
metadata.volumeCount = xlate('numberOfVolumes')
|
||||
metadata.language = xlate('language')
|
||||
metadata.country = xlate('country')
|
||||
metadata.criticalRating = xlate('rating')
|
||||
metadata.tags = xlate('tags')
|
||||
metadata.credits = cbi['credits']
|
||||
metadata.tags = cbi['tags']
|
||||
|
||||
# make sure credits and tags are at least empty lists and not None
|
||||
if metadata.credits is None:
|
||||
@@ -103,33 +98,24 @@ class ComicBookInfo:
|
||||
|
||||
# helper func
|
||||
def assign(cbi_entry, md_entry):
|
||||
if md_entry is not None:
|
||||
if md_entry is not None or isinstance(md_entry, str) and md_entry != "":
|
||||
cbi[cbi_entry] = md_entry
|
||||
|
||||
# helper func
|
||||
def toInt(s):
|
||||
i = None
|
||||
if type(s) in [str, str, int]:
|
||||
try:
|
||||
i = int(s)
|
||||
except ValueError:
|
||||
pass
|
||||
return i
|
||||
|
||||
assign('series', metadata.series)
|
||||
assign('title', metadata.title)
|
||||
assign('issue', metadata.issue)
|
||||
assign('publisher', metadata.publisher)
|
||||
assign('publicationMonth', toInt(metadata.month))
|
||||
assign('publicationYear', toInt(metadata.year))
|
||||
assign('numberOfIssues', toInt(metadata.issueCount))
|
||||
assign('comments', metadata.comments)
|
||||
assign('genre', metadata.genre)
|
||||
assign('volume', toInt(metadata.volume))
|
||||
assign('numberOfVolumes', toInt(metadata.volumeCount))
|
||||
assign('language', utils.getLanguageFromISO(metadata.language))
|
||||
assign('country', metadata.country)
|
||||
assign('rating', metadata.criticalRating)
|
||||
assign('series', utils.xlate(metadata.series))
|
||||
assign('title', utils.xlate(metadata.title))
|
||||
assign('issue', utils.xlate(metadata.issue))
|
||||
assign('publisher', utils.xlate(metadata.publisher))
|
||||
assign('publicationMonth', utils.xlate(metadata.month, True))
|
||||
assign('publicationYear', utils.xlate(metadata.year, True))
|
||||
assign('seriesPublicationYear', utils.xlate(metadata.seriesYear, True))
|
||||
assign('numberOfIssues', utils.xlate(metadata.issueCount, True))
|
||||
assign('comments', utils.xlate(metadata.comments))
|
||||
assign('genre', utils.xlate(metadata.genre))
|
||||
assign('volume', utils.xlate(metadata.volume, True))
|
||||
assign('numberOfVolumes', utils.xlate(metadata.volumeCount, True))
|
||||
assign('language', utils.xlate(utils.getLanguageFromISO(metadata.language)))
|
||||
assign('country', utils.xlate(metadata.country))
|
||||
assign('rating', utils.xlate(metadata.criticalRating))
|
||||
assign('credits', metadata.credits)
|
||||
assign('tags', metadata.tags)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import xml.etree.ElementTree as ET
|
||||
#import zipfile
|
||||
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .issuestring import IssueString
|
||||
from . import utils
|
||||
|
||||
|
||||
@@ -103,6 +104,7 @@ class ComicInfoXml:
|
||||
assign('Year', md.year)
|
||||
assign('Month', md.month)
|
||||
assign('Day', md.day)
|
||||
assign('SeriesYear', md.seriesYear)
|
||||
|
||||
# need to specially process the credits, since they are structured
|
||||
# differently than CIX
|
||||
@@ -206,48 +208,45 @@ class ComicInfoXml:
|
||||
raise 1
|
||||
return None
|
||||
|
||||
metadata = GenericMetadata()
|
||||
md = metadata
|
||||
|
||||
# Helper function
|
||||
def xlate(tag):
|
||||
node = root.find(tag)
|
||||
if node is not None:
|
||||
return node.text
|
||||
else:
|
||||
def get(name):
|
||||
tag = root.find(name)
|
||||
if tag is None:
|
||||
return None
|
||||
return tag.text
|
||||
|
||||
md.series = xlate('Series')
|
||||
md.title = xlate('Title')
|
||||
md.issue = xlate('Number')
|
||||
md.issueCount = xlate('Count')
|
||||
md.volume = xlate('Volume')
|
||||
md.alternateSeries = xlate('AlternateSeries')
|
||||
md.alternateNumber = xlate('AlternateNumber')
|
||||
md.alternateCount = xlate('AlternateCount')
|
||||
md.comments = xlate('Summary')
|
||||
md.notes = xlate('Notes')
|
||||
md.year = xlate('Year')
|
||||
md.month = xlate('Month')
|
||||
md.day = xlate('Day')
|
||||
md.publisher = xlate('Publisher')
|
||||
md.imprint = xlate('Imprint')
|
||||
md.genre = xlate('Genre')
|
||||
md.webLink = xlate('Web')
|
||||
md.language = xlate('LanguageISO')
|
||||
md.format = xlate('Format')
|
||||
md.manga = xlate('Manga')
|
||||
md.characters = xlate('Characters')
|
||||
md.teams = xlate('Teams')
|
||||
md.locations = xlate('Locations')
|
||||
md.pageCount = xlate('PageCount')
|
||||
md.scanInfo = xlate('ScanInformation')
|
||||
md.storyArc = xlate('StoryArc')
|
||||
md.seriesGroup = xlate('SeriesGroup')
|
||||
md.maturityRating = xlate('AgeRating')
|
||||
md = GenericMetadata()
|
||||
|
||||
tmp = xlate('BlackAndWhite')
|
||||
md.blackAndWhite = False
|
||||
md.series = utils.xlate(get('Series'))
|
||||
md.title = utils.xlate(get('Title'))
|
||||
md.issue = IssueString(utils.xlate(get('Number'))).asString()
|
||||
md.issueCount = utils.xlate(get('Count'), True)
|
||||
md.volume = utils.xlate(get('Volume'), True)
|
||||
md.alternateSeries = utils.xlate(get('AlternateSeries'))
|
||||
md.alternateNumber = IssueString(utils.xlate(get('AlternateNumber'))).asString()
|
||||
md.alternateCount = utils.xlate(get('AlternateCount'), True)
|
||||
md.comments = utils.xlate(get('Summary'))
|
||||
md.notes = utils.xlate(get('Notes'))
|
||||
md.year = utils.xlate(get('Year'), True)
|
||||
md.month = utils.xlate(get('Month'), True)
|
||||
md.day = utils.xlate(get('Day'), True)
|
||||
md.seriesYear = utils.xlate(get('SeriesYear'), True)
|
||||
md.publisher = utils.xlate(get('Publisher'))
|
||||
md.imprint = utils.xlate(get('Imprint'))
|
||||
md.genre = utils.xlate(get('Genre'))
|
||||
md.webLink = utils.xlate(get('Web'))
|
||||
md.language = utils.xlate(get('LanguageISO'))
|
||||
md.format = utils.xlate(get('Format'))
|
||||
md.manga = utils.xlate(get('Manga'))
|
||||
md.characters = utils.xlate(get('Characters'))
|
||||
md.teams = utils.xlate(get('Teams'))
|
||||
md.locations = utils.xlate(get('Locations'))
|
||||
md.pageCount = utils.xlate(get('PageCount'), True)
|
||||
md.scanInfo = utils.xlate(get('ScanInformation'))
|
||||
md.storyArc = utils.xlate(get('StoryArc'))
|
||||
md.seriesGroup = utils.xlate(get('SeriesGroup'))
|
||||
md.maturityRating = utils.xlate(get('AgeRating'))
|
||||
|
||||
tmp = utils.xlate(get('BlackAndWhite'))
|
||||
if tmp is not None and tmp.lower() in ["yes", "true", "1"]:
|
||||
md.blackAndWhite = True
|
||||
# Now extract the credit info
|
||||
@@ -261,23 +260,23 @@ class ComicInfoXml:
|
||||
):
|
||||
if n.text is not None:
|
||||
for name in n.text.split(','):
|
||||
metadata.addCredit(name.strip(), n.tag)
|
||||
md.addCredit(name.strip(), n.tag)
|
||||
|
||||
if n.tag == 'CoverArtist':
|
||||
if n.text is not None:
|
||||
for name in n.text.split(','):
|
||||
metadata.addCredit(name.strip(), "Cover")
|
||||
md.addCredit(name.strip(), "Cover")
|
||||
|
||||
# parse page data now
|
||||
pages_node = root.find("Pages")
|
||||
if pages_node is not None:
|
||||
for page in pages_node:
|
||||
metadata.pages.append(page.attrib)
|
||||
md.pages.append(page.attrib)
|
||||
# print page.attrib
|
||||
|
||||
metadata.isEmpty = False
|
||||
md.isEmpty = False
|
||||
|
||||
return metadata
|
||||
return md
|
||||
|
||||
def writeToExternalFile(self, filename, metadata):
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ class GenericMetadata:
|
||||
self.issue = None
|
||||
self.title = None
|
||||
self.publisher = None
|
||||
self.seriesYear = None
|
||||
self.month = None
|
||||
self.year = None
|
||||
self.day = None
|
||||
@@ -132,6 +133,7 @@ class GenericMetadata:
|
||||
assign("issueCount", new_md.issueCount)
|
||||
assign("title", new_md.title)
|
||||
assign("publisher", new_md.publisher)
|
||||
assign("seriesYear", new_md.seriesYear)
|
||||
assign("day", new_md.day)
|
||||
assign("month", new_md.month)
|
||||
assign("year", new_md.year)
|
||||
@@ -263,6 +265,7 @@ class GenericMetadata:
|
||||
add_attr_string("issueCount")
|
||||
add_attr_string("title")
|
||||
add_attr_string("publisher")
|
||||
add_attr_string("seriesYear")
|
||||
add_attr_string("year")
|
||||
add_attr_string("month")
|
||||
add_attr_string("day")
|
||||
|
||||
@@ -21,6 +21,7 @@ import re
|
||||
import platform
|
||||
import locale
|
||||
import codecs
|
||||
import unicodedata
|
||||
|
||||
|
||||
class UtilsVars:
|
||||
@@ -121,6 +122,23 @@ def which(program):
|
||||
return None
|
||||
|
||||
|
||||
def xlate(data, isInt=False):
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return None
|
||||
if data is None or data == "":
|
||||
return None
|
||||
if isInt:
|
||||
i = str(data).translate(Default(zip((ord(c) for c in "1234567890"),"1234567890")))
|
||||
if i == "0":
|
||||
return "0"
|
||||
if i is "":
|
||||
return None
|
||||
return int(i)
|
||||
else:
|
||||
return str(data)
|
||||
|
||||
|
||||
def removearticles(text):
|
||||
text = text.lower()
|
||||
articles = ['and', 'a', '&', 'issue', 'the']
|
||||
@@ -131,19 +149,24 @@ def removearticles(text):
|
||||
|
||||
newText = newText[:-1]
|
||||
|
||||
# now get rid of some other junk
|
||||
newText = newText.replace(":", "")
|
||||
newText = newText.replace(",", "")
|
||||
newText = newText.replace("-", " ")
|
||||
|
||||
# since the CV API changed, searches for series names with periods
|
||||
# now explicitly require the period to be in the search key,
|
||||
# so the line below is removed (for now)
|
||||
#newText = newText.replace(".", "")
|
||||
|
||||
return newText
|
||||
|
||||
|
||||
def sanitize_title(text):
|
||||
# normalize unicode and convert to ascii. Does not work for everything eg ½ to 1⁄2 not 1/2
|
||||
# this will probably cause issues with titles in other character sets e.g. chinese, japanese
|
||||
text = unicodedata.normalize('NFKD', text).encode('ascii', 'ignore').decode('ascii')
|
||||
# comicvine keeps apostrophes a part of the word
|
||||
text = text.replace("'", "")
|
||||
text = text.replace("\"", "")
|
||||
# comicvine ignores punctuation and accents
|
||||
text = re.sub(r'[^A-Za-z0-9]+',' ', text)
|
||||
# remove extra space and articles and all lower case
|
||||
text = removearticles(text).lower().strip()
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def unique_file(file_name):
|
||||
counter = 1
|
||||
# returns ('/path/file', '.ext')
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
import platform
|
||||
from os.path import join
|
||||
from comictaggerlib import ctversion
|
||||
|
||||
enable_console = False
|
||||
binaries = []
|
||||
block_cipher = None
|
||||
|
||||
binaries = [
|
||||
('./unrar/libunrar.so', './'),
|
||||
]
|
||||
|
||||
if platform.system() == "Windows":
|
||||
from site import getsitepackages
|
||||
sitepackages = getsitepackages()[1]
|
||||
# add ssl qt libraries not discovered automatically
|
||||
binaries.extend([
|
||||
('%s/PyQt5/Qt/bin/libeay32.dll' % sitepackages, './PyQt5/Qt/bin'),
|
||||
('%s/PyQt5/Qt/bin/ssleay32.dll' % sitepackages, './PyQt5/Qt/bin')
|
||||
])
|
||||
enable_console = True
|
||||
|
||||
a = Analysis(['comictagger.py'],
|
||||
binaries=binaries,
|
||||
@@ -41,7 +34,7 @@ exe = EXE(pyz,
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
console=enable_console,
|
||||
icon="windows/app.ico" )
|
||||
|
||||
app = BUNDLE(exe,
|
||||
@@ -49,8 +42,9 @@ app = BUNDLE(exe,
|
||||
icon='mac/app.icns',
|
||||
info_plist={
|
||||
'NSHighResolutionCapable': 'True',
|
||||
'NSRequiresAquaSystemAppearance': 'False',
|
||||
'CFBundleDisplayName': 'ComicTagger',
|
||||
'CFBundleShortVersionString': ctversion.version,
|
||||
'CFBundleVersion': ctversion.version
|
||||
},
|
||||
bundle_identifier=None)
|
||||
bundle_identifier=None)
|
||||
|
||||
@@ -507,19 +507,31 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
renamer.setTemplate(settings.rename_template)
|
||||
renamer.setIssueZeroPadding(settings.rename_issue_number_padding)
|
||||
renamer.setSmartCleanup(settings.rename_use_smart_string_cleanup)
|
||||
renamer.move = settings.rename_move_dir
|
||||
|
||||
new_name = renamer.determineName(filename, ext=new_ext)
|
||||
|
||||
if new_name == os.path.basename(filename):
|
||||
print(msg_hdr + "Filename is already good!", file=sys.stderr)
|
||||
try:
|
||||
new_name = renamer.determineName(filename, ext=new_ext)
|
||||
except Exception as e:
|
||||
print(msg_hdr + "Invalid format string!\nYour rename template is invalid!\n\n"
|
||||
"{}\n\nPlease consult the template help in the settings "
|
||||
"and the documentation on the format at "
|
||||
"https://docs.python.org/3/library/string.html#format-string-syntax", file=sys.stderr)
|
||||
return
|
||||
|
||||
folder = os.path.dirname(os.path.abspath(filename))
|
||||
if settings.rename_move_dir and len(settings.rename_dir.strip()) > 3:
|
||||
folder = settings.rename_dir.strip()
|
||||
|
||||
new_abs_path = utils.unique_file(os.path.join(folder, new_name))
|
||||
|
||||
if os.path.join(folder, new_name) == os.path.abspath(filename):
|
||||
print(msg_hdr + "Filename is already good!", file=sys.stderr)
|
||||
return
|
||||
|
||||
suffix = ""
|
||||
if not opts.dryrun:
|
||||
# rename the file
|
||||
os.makedirs(os.path.dirname(new_abs_path), 0o777, True)
|
||||
os.rename(filename, new_abs_path)
|
||||
else:
|
||||
suffix = " (dry-run, no change)"
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import requests
|
||||
import re
|
||||
import time
|
||||
import datetime
|
||||
@@ -104,9 +103,6 @@ class ComicVineTalker(QObject):
|
||||
|
||||
self.log_func = None
|
||||
|
||||
# always use a tls context for urlopen
|
||||
self.ssl = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
|
||||
def setLogFunc(self, log_func):
|
||||
self.log_func = log_func
|
||||
|
||||
@@ -124,23 +120,20 @@ class ComicVineTalker(QObject):
|
||||
year = None
|
||||
if date_str is not None:
|
||||
parts = date_str.split('-')
|
||||
year = parts[0]
|
||||
year = utils.xlate(parts[0], True)
|
||||
if len(parts) > 1:
|
||||
month = parts[1]
|
||||
month = utils.xlate(parts[1], True)
|
||||
if len(parts) > 2:
|
||||
day = parts[2]
|
||||
day = utils.xlate(parts[2], True)
|
||||
return day, month, year
|
||||
|
||||
def testKey(self, key):
|
||||
|
||||
try:
|
||||
test_url = self.api_base_url + "/issue/1/?api_key=" + \
|
||||
key + "&format=json&field_list=name"
|
||||
resp = urllib.request.urlopen(test_url, context=self.ssl)
|
||||
content = resp.read()
|
||||
|
||||
cv_response = json.loads(content.decode('utf-8'))
|
||||
|
||||
test_url = self.api_base_url + "/issue/1/?api_key=" + key + "&format=json&field_list=name"
|
||||
|
||||
cv_response = requests.get(test_url, headers={'user-agent': 'comictagger/' + ctversion.version}).json()
|
||||
|
||||
# Bogus request, but if the key is wrong, you get error 100: "Invalid
|
||||
# API Key"
|
||||
return cv_response['status_code'] != 100
|
||||
@@ -152,14 +145,13 @@ class ComicVineTalker(QObject):
|
||||
sleep for a bit and retry.
|
||||
"""
|
||||
|
||||
def getCVContent(self, url):
|
||||
def getCVContent(self, url, params):
|
||||
total_time_waited = 0
|
||||
limit_wait_time = 1
|
||||
counter = 0
|
||||
wait_times = [1, 2, 3, 4]
|
||||
while True:
|
||||
content = self.getUrlContent(url)
|
||||
cv_response = json.loads(content.decode('utf-8'))
|
||||
cv_response = self.getUrlContent(url, params)
|
||||
if self.wait_for_rate_limit and cv_response[
|
||||
'status_code'] == ComicVineTalkerException.RateLimit:
|
||||
self.writeLog(
|
||||
@@ -184,25 +176,24 @@ class ComicVineTalker(QObject):
|
||||
break
|
||||
return cv_response
|
||||
|
||||
def getUrlContent(self, url):
|
||||
def getUrlContent(self, url, params):
|
||||
# connect to server:
|
||||
# if there is a 500 error, try a few more times before giving up
|
||||
# any other error, just bail
|
||||
#print("---", url)
|
||||
for tries in range(3):
|
||||
try:
|
||||
resp = urllib.request.urlopen(url, context=self.ssl)
|
||||
return resp.read()
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.getcode() == 500:
|
||||
resp = requests.get(url, params=params, headers={'user-agent': 'comictagger/' + ctversion.version})
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
if resp.status_code == 500:
|
||||
self.writeLog("Try #{0}: ".format(tries + 1))
|
||||
time.sleep(1)
|
||||
self.writeLog(str(e) + "\n")
|
||||
|
||||
if e.getcode() != 500:
|
||||
self.writeLog(str(resp.status_code) + "\n")
|
||||
else:
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.writeLog(str(e) + "\n")
|
||||
raise ComicVineTalkerException(
|
||||
ComicVineTalkerException.Network, "Network Error!")
|
||||
@@ -212,8 +203,8 @@ class ComicVineTalker(QObject):
|
||||
|
||||
def searchForSeries(self, series_name, callback=None, refresh_cache=False):
|
||||
|
||||
# remove cruft from the search string
|
||||
series_name = utils.removearticles(series_name).lower().strip()
|
||||
# Sanitize the series name for comicvine searching, comicvine search ignore symbols
|
||||
search_series_name = utils.sanitize_title(series_name)
|
||||
|
||||
# before we search online, look in our cache, since we might have
|
||||
# done this same search recently
|
||||
@@ -224,19 +215,17 @@ class ComicVineTalker(QObject):
|
||||
if len(cached_search_results) > 0:
|
||||
return cached_search_results
|
||||
|
||||
original_series_name = series_name
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'format': 'json',
|
||||
'resources': 'volume',
|
||||
'query': search_series_name,
|
||||
'field_list': 'volume,name,id,start_year,publisher,image,description,count_of_issues',
|
||||
'page': 1,
|
||||
'limit': 100
|
||||
}
|
||||
|
||||
# Split and rejoin to remove extra internal spaces
|
||||
query_word_list = series_name.split()
|
||||
query_string = " ".join( query_word_list ).strip()
|
||||
#print ("Query string = ", query_string)
|
||||
|
||||
query_string = urllib.parse.quote_plus(query_string.encode("utf-8"))
|
||||
|
||||
search_url = self.api_base_url + "/search/?api_key=" + self.api_key + "&format=json&resources=volume&query=" + \
|
||||
query_string + \
|
||||
"&field_list=name,id,start_year,publisher,image,description,count_of_issues"
|
||||
cv_response = self.getCVContent(search_url + "&page=1")
|
||||
cv_response = self.getCVContent(self.api_base_url + "/search", params)
|
||||
|
||||
search_results = list()
|
||||
|
||||
@@ -249,15 +238,15 @@ class ComicVineTalker(QObject):
|
||||
# 8 Dec 2018 - Comic Vine changed query results again. Terms are now
|
||||
# ORed together, and we get thousands of results. Good news is the
|
||||
# results are sorted by relevance, so we can be smart about halting
|
||||
# the search.
|
||||
# the search.
|
||||
# 1. Don't fetch more than some sane amount of pages.
|
||||
max_results = 500
|
||||
max_results = 500
|
||||
# 2. Halt when not all of our search terms are present in a result
|
||||
# 3. Halt when the results contain more (plus threshold) words than
|
||||
# our search
|
||||
result_word_count_max = len(query_word_list) + 3
|
||||
result_word_count_max = len(search_series_name.split()) + 3
|
||||
|
||||
total_result_count = min(total_result_count, max_results)
|
||||
total_result_count = min(total_result_count, max_results)
|
||||
|
||||
if callback is None:
|
||||
self.writeLog(
|
||||
@@ -276,18 +265,20 @@ class ComicVineTalker(QObject):
|
||||
|
||||
last_result = search_results[-1]['name']
|
||||
|
||||
# Sanitize the series name for comicvine searching, comicvine search ignore symbols
|
||||
last_result = utils.sanitize_title(last_result)
|
||||
|
||||
# See if the last result's name has all the of the search terms.
|
||||
# if not, break out of this, loop, we're done.
|
||||
#print("Searching for {} in '{}'".format(query_word_list, last_result))
|
||||
for term in query_word_list:
|
||||
for term in search_series_name.split():
|
||||
if term not in last_result.lower():
|
||||
#print("Term '{}' not in last result. Halting search result fetching".format(term))
|
||||
stop_searching = True
|
||||
break
|
||||
|
||||
# Also, stop searching when the word count of last results is too much longer
|
||||
# than our search terms list
|
||||
if len(utils.removearticles(last_result).split()) > result_word_count_max:
|
||||
# than our search terms list
|
||||
if len(last_result) > result_word_count_max:
|
||||
#print("Last result '{}' is too long. Halting search result fetching".format(last_result))
|
||||
stop_searching = True
|
||||
|
||||
@@ -301,7 +292,8 @@ class ComicVineTalker(QObject):
|
||||
total_result_count))
|
||||
page += 1
|
||||
|
||||
cv_response = self.getCVContent(search_url + "&page=" + str(page))
|
||||
params['page'] = page
|
||||
cv_response = self.getCVContent(self.api_base_url + "/search", params)
|
||||
|
||||
search_results.extend(cv_response['results'])
|
||||
current_result_count += cv_response['number_of_page_results']
|
||||
@@ -313,8 +305,11 @@ class ComicVineTalker(QObject):
|
||||
# (iterate backwards for easy removal)
|
||||
for i in range(len(search_results) - 1, -1, -1):
|
||||
record = search_results[i]
|
||||
for term in query_word_list:
|
||||
if term not in record['name'].lower():
|
||||
# Sanitize the series name for comicvine searching, comicvine search ignore symbols
|
||||
recordName = utils.sanitize_title(record['name'])
|
||||
for term in search_series_name.split():
|
||||
|
||||
if term not in recordName:
|
||||
del search_results[i]
|
||||
break
|
||||
|
||||
@@ -325,7 +320,7 @@ class ComicVineTalker(QObject):
|
||||
#print(u"{0}: {1} ({2})".format(search_results['results'][0]['id'], search_results['results'][0]['name'] , search_results['results'][0]['start_year']))
|
||||
|
||||
# cache these search results
|
||||
cvc.add_search_results(original_series_name, search_results)
|
||||
cvc.add_search_results(series_name, search_results)
|
||||
|
||||
return search_results
|
||||
|
||||
@@ -339,11 +334,14 @@ class ComicVineTalker(QObject):
|
||||
if cached_volume_result is not None:
|
||||
return cached_volume_result
|
||||
|
||||
volume_url = self.api_base_url + "/volume/" + CVTypeID.Volume + "-" + \
|
||||
str(series_id) + "/?api_key=" + self.api_key + \
|
||||
"&field_list=name,id,start_year,publisher,count_of_issues&format=json"
|
||||
volume_url = self.api_base_url + "/volume/" + CVTypeID.Volume + "-" + str(series_id)
|
||||
|
||||
cv_response = self.getCVContent(volume_url)
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'format': 'json',
|
||||
'field_list': 'name,id,start_year,publisher,count_of_issues'
|
||||
}
|
||||
cv_response = self.getCVContent(volume_url, params)
|
||||
|
||||
volume_results = cv_response['results']
|
||||
|
||||
@@ -361,11 +359,13 @@ class ComicVineTalker(QObject):
|
||||
if cached_volume_issues_result is not None:
|
||||
return cached_volume_issues_result
|
||||
|
||||
#---------------------------------
|
||||
issues_url = self.api_base_url + "/issues/" + "?api_key=" + self.api_key + "&filter=volume:" + \
|
||||
str(series_id) + \
|
||||
"&field_list=id,volume,issue_number,name,image,cover_date,site_detail_url,description&format=json"
|
||||
cv_response = self.getCVContent(issues_url)
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'filter': 'volume:' + str(series_id),
|
||||
'format': 'json',
|
||||
'field_list': 'id,volume,issue_number,name,image,cover_date,site_detail_url,description'
|
||||
}
|
||||
cv_response = self.getCVContent(self.api_base_url + "/issues/", params)
|
||||
|
||||
#------------------------------------
|
||||
|
||||
@@ -385,9 +385,8 @@ class ComicVineTalker(QObject):
|
||||
page += 1
|
||||
offset += cv_response['number_of_page_results']
|
||||
|
||||
# print issues_url+ "&offset="+str(offset)
|
||||
cv_response = self.getCVContent(
|
||||
issues_url + "&offset=" + str(offset))
|
||||
params['offset'] = offset
|
||||
cv_response = self.getCVContent(self.api_base_url + "/issues/", params)
|
||||
|
||||
volume_issues_result.extend(cv_response['results'])
|
||||
current_result_count += cv_response['number_of_page_results']
|
||||
@@ -398,26 +397,24 @@ class ComicVineTalker(QObject):
|
||||
|
||||
return volume_issues_result
|
||||
|
||||
def fetchIssuesByVolumeIssueNumAndYear(
|
||||
self, volume_id_list, issue_number, year):
|
||||
volume_filter = "volume:"
|
||||
def fetchIssuesByVolumeIssueNumAndYear(self, volume_id_list, issue_number, year):
|
||||
volume_filter = ""
|
||||
for vid in volume_id_list:
|
||||
volume_filter += str(vid) + "|"
|
||||
filter = "volume:{},issue_number:{}".format(volume_filter, issue_number)
|
||||
|
||||
year_filter = ""
|
||||
if year is not None and str(year).isdigit():
|
||||
year_filter = ",cover_date:{0}-1-1|{1}-1-1".format(
|
||||
year, int(year) + 1)
|
||||
intYear = utils.xlate(year, True)
|
||||
if intYear is not None:
|
||||
filter += ",cover_date:{}-1-1|{}-1-1".format(intYear, intYear + 1)
|
||||
|
||||
issue_number = urllib.parse.quote_plus(str(issue_number).encode("utf-8"))
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'format': 'json',
|
||||
'field_list': 'id,volume,issue_number,name,image,cover_date,site_detail_url,description',
|
||||
'filter': filter
|
||||
}
|
||||
|
||||
filter = "&filter=" + volume_filter + \
|
||||
year_filter + ",issue_number:" + issue_number
|
||||
|
||||
issues_url = self.api_base_url + "/issues/" + "?api_key=" + self.api_key + filter + \
|
||||
"&field_list=id,volume,issue_number,name,image,cover_date,site_detail_url,description&format=json"
|
||||
|
||||
cv_response = self.getCVContent(issues_url)
|
||||
cv_response = self.getCVContent(self.api_base_url + "/issues", params)
|
||||
|
||||
#------------------------------------
|
||||
|
||||
@@ -437,9 +434,8 @@ class ComicVineTalker(QObject):
|
||||
page += 1
|
||||
offset += cv_response['number_of_page_results']
|
||||
|
||||
# print issues_url+ "&offset="+str(offset)
|
||||
cv_response = self.getCVContent(
|
||||
issues_url + "&offset=" + str(offset))
|
||||
params['offset'] = offset
|
||||
cv_response = self.getCVContent(self.api_base_url + "/issues/", params)
|
||||
|
||||
filtered_issues_result.extend(cv_response['results'])
|
||||
current_result_count += cv_response['number_of_page_results']
|
||||
@@ -463,11 +459,12 @@ class ComicVineTalker(QObject):
|
||||
break
|
||||
|
||||
if (found):
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \
|
||||
str(record['id']) + "/?api_key=" + \
|
||||
self.api_key + "&format=json"
|
||||
|
||||
cv_response = self.getCVContent(issue_url)
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(record['id'])
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'format': 'json'
|
||||
}
|
||||
cv_response = self.getCVContent(issue_url, params)
|
||||
issue_results = cv_response['results']
|
||||
|
||||
else:
|
||||
@@ -479,9 +476,12 @@ class ComicVineTalker(QObject):
|
||||
|
||||
def fetchIssueDataByIssueID(self, issue_id, settings):
|
||||
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \
|
||||
str(issue_id) + "/?api_key=" + self.api_key + "&format=json"
|
||||
cv_response = self.getCVContent(issue_url)
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(issue_id)
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'format': 'json'
|
||||
}
|
||||
cv_response = self.getCVContent(issue_url, params)
|
||||
|
||||
issue_results = cv_response['results']
|
||||
|
||||
@@ -497,21 +497,20 @@ class ComicVineTalker(QObject):
|
||||
# Now, map the Comic Vine data to generic metadata
|
||||
metadata = GenericMetadata()
|
||||
|
||||
metadata.series = issue_results['volume']['name']
|
||||
metadata.series = utils.xlate(issue_results['volume']['name'])
|
||||
metadata.issue = IssueString(issue_results['issue_number']).asString()
|
||||
metadata.title = utils.xlate(issue_results['name'])
|
||||
|
||||
num_s = IssueString(issue_results['issue_number']).asString()
|
||||
metadata.issue = num_s
|
||||
metadata.title = issue_results['name']
|
||||
if volume_results['publisher'] is not None:
|
||||
metadata.publisher = utils.xlate(volume_results['publisher']['name'])
|
||||
metadata.day, metadata.month, metadata.year = self.parseDateStr(issue_results['cover_date'])
|
||||
|
||||
metadata.publisher = volume_results['publisher']['name']
|
||||
metadata.day, metadata.month, metadata.year = self.parseDateStr(
|
||||
issue_results['cover_date'])
|
||||
|
||||
#metadata.issueCount = volume_results['count_of_issues']
|
||||
metadata.seriesYear = utils.xlate(volume_results['start_year'])
|
||||
metadata.issueCount = utils.xlate(volume_results['count_of_issues'])
|
||||
metadata.comments = self.cleanup_html(
|
||||
issue_results['description'], settings.remove_html_tables)
|
||||
if settings.use_series_start_as_volume:
|
||||
metadata.volume = volume_results['start_year']
|
||||
metadata.volume = utils.xlate(volume_results['start_year'])
|
||||
|
||||
metadata.notes = "Tagged with ComicTagger {0} using info from Comic Vine on {1}. [Issue ID {2}]".format(
|
||||
ctversion.version,
|
||||
@@ -672,9 +671,15 @@ class ComicVineTalker(QObject):
|
||||
if cached_details['image_url'] is not None:
|
||||
return cached_details
|
||||
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \
|
||||
str(issue_id) + "/?api_key=" + self.api_key + \
|
||||
"&format=json&field_list=image,cover_date,site_detail_url"
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(issue_id)
|
||||
|
||||
params = {
|
||||
'api_key': self.api_key,
|
||||
'format': 'json',
|
||||
'field_list': 'image,cover_date,site_detail_url'
|
||||
}
|
||||
|
||||
cv_response = self.getCVContent(issue_url, params)
|
||||
|
||||
details = dict()
|
||||
details['image_url'] = None
|
||||
@@ -682,8 +687,6 @@ class ComicVineTalker(QObject):
|
||||
details['cover_date'] = None
|
||||
details['site_detail_url'] = None
|
||||
|
||||
cv_response = self.getCVContent(issue_url)
|
||||
|
||||
details['image_url'] = cv_response['results']['image']['super_url']
|
||||
details['thumb_image_url'] = cv_response[
|
||||
'results']['image']['thumb_url']
|
||||
@@ -718,8 +721,7 @@ class ComicVineTalker(QObject):
|
||||
return url_list
|
||||
|
||||
# scrape the CV issue page URL to get the alternate cover URLs
|
||||
resp = urllib.request.urlopen(issue_page_url, context=self.ssl)
|
||||
content = resp.read()
|
||||
content = requests.get(issue_page_url, headers={'user-agent': 'comictagger/' + ctversion.version}).text
|
||||
alt_cover_url_list = self.parseOutAltCoverUrls(content)
|
||||
|
||||
# cache this alt cover URL list
|
||||
@@ -729,9 +731,9 @@ class ComicVineTalker(QObject):
|
||||
|
||||
def parseOutAltCoverUrls(self, page_html):
|
||||
soup = BeautifulSoup(page_html, "html.parser")
|
||||
|
||||
|
||||
alt_cover_url_list = []
|
||||
|
||||
|
||||
# Using knowledge of the layout of the Comic Vine issue page here:
|
||||
# look for the divs that are in the classes 'imgboxart' and
|
||||
# 'issue-cover'
|
||||
@@ -740,15 +742,15 @@ class ComicVineTalker(QObject):
|
||||
for d in div_list:
|
||||
if 'class' in d.attrs:
|
||||
c = d['class']
|
||||
if ('imgboxart' in c and
|
||||
if ('imgboxart' in c and
|
||||
'issue-cover' in c and
|
||||
d.img['src'].startswith("http")
|
||||
):
|
||||
|
||||
|
||||
covers_found += 1
|
||||
if covers_found != 1:
|
||||
alt_cover_url_list.append(d.img['src'])
|
||||
|
||||
|
||||
return alt_cover_url_list
|
||||
|
||||
def fetchCachedAlternateCoverURLs(self, issue_id):
|
||||
|
||||
@@ -17,19 +17,96 @@
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
import sys
|
||||
import string
|
||||
|
||||
from pathvalidate import sanitize_filepath
|
||||
|
||||
from . import utils
|
||||
from .issuestring import IssueString
|
||||
|
||||
|
||||
class MetadataFormatter(string.Formatter):
|
||||
def __init__(self, smart_cleanup=False):
|
||||
super().__init__()
|
||||
self.smart_cleanup = smart_cleanup
|
||||
|
||||
def format_field(self, value, format_spec):
|
||||
if value is None or value == "":
|
||||
return ""
|
||||
return super().format_field(value, format_spec)
|
||||
|
||||
def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
|
||||
auto_arg_index=0):
|
||||
if recursion_depth < 0:
|
||||
raise ValueError('Max string recursion exceeded')
|
||||
result = []
|
||||
lstrip = False
|
||||
for literal_text, field_name, format_spec, conversion in \
|
||||
self.parse(format_string):
|
||||
|
||||
# output the literal text
|
||||
if literal_text:
|
||||
if lstrip:
|
||||
result.append(literal_text.lstrip("-_)}]#"))
|
||||
else:
|
||||
result.append(literal_text)
|
||||
lstrip = False
|
||||
# if there's a field, output it
|
||||
if field_name is not None:
|
||||
# this is some markup, find the object and do
|
||||
# the formatting
|
||||
|
||||
# handle arg indexing when empty field_names are given.
|
||||
if field_name == '':
|
||||
if auto_arg_index is False:
|
||||
raise ValueError('cannot switch from manual field '
|
||||
'specification to automatic field '
|
||||
'numbering')
|
||||
field_name = str(auto_arg_index)
|
||||
auto_arg_index += 1
|
||||
elif field_name.isdigit():
|
||||
if auto_arg_index:
|
||||
raise ValueError('cannot switch from manual field '
|
||||
'specification to automatic field '
|
||||
'numbering')
|
||||
# disable auto arg incrementing, if it gets
|
||||
# used later on, then an exception will be raised
|
||||
auto_arg_index = False
|
||||
|
||||
# given the field_name, find the object it references
|
||||
# and the argument it came from
|
||||
obj, arg_used = self.get_field(field_name, args, kwargs)
|
||||
used_args.add(arg_used)
|
||||
|
||||
# do any conversion on the resulting object
|
||||
obj = self.convert_field(obj, conversion)
|
||||
|
||||
# expand the format spec, if needed
|
||||
format_spec, auto_arg_index = self._vformat(
|
||||
format_spec, args, kwargs,
|
||||
used_args, recursion_depth-1,
|
||||
auto_arg_index=auto_arg_index)
|
||||
|
||||
# format the object and append to the result
|
||||
fmtObj = self.format_field(obj, format_spec)
|
||||
if fmtObj == "" and len(result) > 0 and self.smart_cleanup:
|
||||
lstrip = True
|
||||
result.pop()
|
||||
result.append(fmtObj)
|
||||
|
||||
return ''.join(result), auto_arg_index
|
||||
|
||||
|
||||
class FileRenamer:
|
||||
|
||||
def __init__(self, metadata):
|
||||
self.setMetadata(metadata)
|
||||
self.setTemplate(
|
||||
"%series% v%volume% #%issue% (of %issuecount%) (%year%)")
|
||||
"{publisher}/{series}/{series} v{volume} #{issue} (of {issueCount}) ({year})")
|
||||
self.smart_cleanup = True
|
||||
self.issue_zero_padding = 3
|
||||
self.move = False
|
||||
|
||||
def setMetadata(self, metadata):
|
||||
self.metdata = metadata
|
||||
@@ -43,114 +120,37 @@ class FileRenamer:
|
||||
def setTemplate(self, template):
|
||||
self.template = template
|
||||
|
||||
def replaceToken(self, text, value, token):
|
||||
# helper func
|
||||
def isToken(word):
|
||||
return (word[0] == "%" and word[-1:] == "%")
|
||||
|
||||
if value is not None:
|
||||
return text.replace(token, str(value))
|
||||
else:
|
||||
if self.smart_cleanup:
|
||||
# smart cleanup means we want to remove anything appended to token if it's empty
|
||||
# (e.g "#%issue%" or "v%volume%")
|
||||
# (TODO: This could fail if there is more than one token appended together, I guess)
|
||||
text_list = text.split()
|
||||
|
||||
# special case for issuecount, remove preceding non-token word,
|
||||
# as in "...(of %issuecount%)..."
|
||||
if token == '%issuecount%':
|
||||
for idx, word in enumerate(text_list):
|
||||
if token in word and not isToken(text_list[idx - 1]):
|
||||
text_list[idx - 1] = ""
|
||||
|
||||
text_list = [x for x in text_list if token not in x]
|
||||
return " ".join(text_list)
|
||||
else:
|
||||
return text.replace(token, "")
|
||||
|
||||
def determineName(self, filename, ext=None):
|
||||
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return "{" + key + "}"
|
||||
md = self.metdata
|
||||
new_name = self.template
|
||||
preferred_encoding = utils.get_actual_preferred_encoding()
|
||||
|
||||
# print(u"{0}".format(md))
|
||||
|
||||
new_name = self.replaceToken(new_name, md.series, '%series%')
|
||||
new_name = self.replaceToken(new_name, md.volume, '%volume%')
|
||||
# padding for issue
|
||||
md.issue = IssueString(md.issue).asString(pad=self.issue_zero_padding)
|
||||
|
||||
if md.issue is not None:
|
||||
issue_str = "{0}".format(
|
||||
IssueString(md.issue).asString(pad=self.issue_zero_padding))
|
||||
else:
|
||||
issue_str = None
|
||||
new_name = self.replaceToken(new_name, issue_str, '%issue%')
|
||||
template = self.template
|
||||
|
||||
new_name = self.replaceToken(new_name, md.issueCount, '%issuecount%')
|
||||
new_name = self.replaceToken(new_name, md.year, '%year%')
|
||||
new_name = self.replaceToken(new_name, md.publisher, '%publisher%')
|
||||
new_name = self.replaceToken(new_name, md.title, '%title%')
|
||||
new_name = self.replaceToken(new_name, md.month, '%month%')
|
||||
month_name = None
|
||||
if md.month is not None:
|
||||
if (isinstance(md.month, str) and md.month.isdigit()) or isinstance(
|
||||
md.month, int):
|
||||
if int(md.month) in range(1, 13):
|
||||
dt = datetime.datetime(1970, int(md.month), 1, 0, 0)
|
||||
#month_name = dt.strftime("%B".encode(preferred_encoding)).decode(preferred_encoding)
|
||||
month_name = dt.strftime("%B")
|
||||
new_name = self.replaceToken(new_name, month_name, '%month_name%')
|
||||
pathComponents = template.split(os.sep)
|
||||
new_name = ""
|
||||
|
||||
new_name = self.replaceToken(new_name, md.genre, '%genre%')
|
||||
new_name = self.replaceToken(new_name, md.language, '%language_code%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.criticalRating, '%criticalrating%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.alternateSeries, '%alternateseries%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.alternateNumber, '%alternatenumber%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.alternateCount, '%alternatecount%')
|
||||
new_name = self.replaceToken(new_name, md.imprint, '%imprint%')
|
||||
new_name = self.replaceToken(new_name, md.format, '%format%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.maturityRating, '%maturityrating%')
|
||||
new_name = self.replaceToken(new_name, md.storyArc, '%storyarc%')
|
||||
new_name = self.replaceToken(new_name, md.seriesGroup, '%seriesgroup%')
|
||||
new_name = self.replaceToken(new_name, md.scanInfo, '%scaninfo%')
|
||||
fmt = MetadataFormatter(self.smart_cleanup)
|
||||
for Component in pathComponents:
|
||||
new_name = os.path.join(new_name, fmt.vformat(Component, args=[], kwargs=Default(vars(md))).replace("/", "-"))
|
||||
|
||||
if self.smart_cleanup:
|
||||
|
||||
# remove empty braces,brackets, parentheses
|
||||
new_name = re.sub("\(\s*[-:]*\s*\)", "", new_name)
|
||||
new_name = re.sub("\[\s*[-:]*\s*\]", "", new_name)
|
||||
new_name = re.sub("\{\s*[-:]*\s*\}", "", new_name)
|
||||
|
||||
# remove duplicate spaces
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
# remove remove duplicate -, _,
|
||||
new_name = re.sub("[-_]{2,}\s+", "-- ", new_name)
|
||||
new_name = re.sub("(\s--)+", " --", new_name)
|
||||
new_name = re.sub("(\s-)+", " -", new_name)
|
||||
|
||||
# remove dash or double dash at end of line
|
||||
new_name = re.sub("[-]{1,2}\s*$", "", new_name)
|
||||
|
||||
# remove duplicate spaces (again!)
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
if ext is None:
|
||||
if ext is None or ext == "":
|
||||
ext = os.path.splitext(filename)[1]
|
||||
|
||||
new_name += ext
|
||||
|
||||
# some tweaks to keep various filesystems happy
|
||||
new_name = new_name.replace("/", "-")
|
||||
new_name = new_name.replace(" :", " -")
|
||||
new_name = new_name.replace(": ", " - ")
|
||||
new_name = new_name.replace(":", "-")
|
||||
new_name = new_name.replace("?", "")
|
||||
|
||||
return new_name
|
||||
# remove padding
|
||||
md.issue = IssueString(md.issue).asString()
|
||||
if self.move:
|
||||
return sanitize_filepath(new_name.strip())
|
||||
else:
|
||||
return os.path.basename(sanitize_filepath(new_name.strip()))
|
||||
|
||||
@@ -216,36 +216,12 @@ class FileSelectionList(QWidget):
|
||||
progdialog.hide()
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
if (self.settings.show_no_unrar_warning and
|
||||
self.settings.unrar_lib_path == "" and
|
||||
not ComicTaggerSettings.haveOwnUnrarLib()):
|
||||
for f in filelist:
|
||||
ext = os.path.splitext(f)[1].lower()
|
||||
if ext == ".rar" or ext == ".cbr":
|
||||
checked = OptionalMessageDialog.msg(self, "No UnRAR Ability",
|
||||
"""
|
||||
It looks like you've tried to open at least one CBR or RAR file.<br><br>
|
||||
In order for ComicTagger to read this kind of file, you will have to configure
|
||||
the location of the unrar library in the settings. Until then, ComicTagger
|
||||
will not be able read these kind of files. See the "RAR Tools" tab in the
|
||||
settings/preferences for more info.
|
||||
"""
|
||||
)
|
||||
self.settings.show_no_unrar_warning = not checked
|
||||
break
|
||||
|
||||
if firstAdded is not None:
|
||||
self.twList.selectRow(firstAdded)
|
||||
else:
|
||||
if len(pathlist) == 1 and os.path.isfile(pathlist[0]):
|
||||
ext = os.path.splitext(pathlist[0])[1].lower()
|
||||
if ext == ".rar" or ext == ".cbr" and self.settings.unrar_lib_path == "":
|
||||
QMessageBox.information(self, self.tr("File Open"), self.tr(
|
||||
"Selected file seems to be a rar file, "
|
||||
"and can't be read until the unrar library is configured."))
|
||||
else:
|
||||
QMessageBox.information(self, self.tr("File Open"), self.tr(
|
||||
"Selected file doesn't seem to be a comic archive."))
|
||||
QMessageBox.information(self, self.tr("File Open"), self.tr(
|
||||
"Selected file doesn't seem to be a comic archive."))
|
||||
else:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
|
||||
@@ -19,9 +19,7 @@ import os
|
||||
import datetime
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import ssl
|
||||
#import urllib2
|
||||
import requests
|
||||
|
||||
try:
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
@@ -46,6 +44,7 @@ except ImportError:
|
||||
pass
|
||||
|
||||
from .settings import ComicTaggerSettings
|
||||
from . import ctversion
|
||||
|
||||
|
||||
class ImageFetcherException(Exception):
|
||||
@@ -66,9 +65,6 @@ class ImageFetcher(QObject):
|
||||
if not os.path.exists(self.db_file):
|
||||
self.create_image_db()
|
||||
|
||||
# always use a tls context for urlopen
|
||||
self.ssl = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
|
||||
def clearCache(self):
|
||||
os.unlink(self.db_file)
|
||||
if os.path.isdir(self.cache_folder):
|
||||
@@ -90,7 +86,8 @@ class ImageFetcher(QObject):
|
||||
if blocking:
|
||||
if image_data is None:
|
||||
try:
|
||||
image_data = urllib.request.urlopen(url, context=self.ssl).read()
|
||||
print(url)
|
||||
image_data = requests.get(url, headers={'user-agent': 'comictagger/' + ctversion.version}).content
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise ImageFetcherException("Network Error!")
|
||||
|
||||
@@ -51,7 +51,6 @@ class ImageHasher(object):
|
||||
image = self.image.resize(
|
||||
(self.width, self.height), Image.ANTIALIAS).convert("L")
|
||||
except Exception as e:
|
||||
sys.exc_clear()
|
||||
print("average_hash error:", e)
|
||||
return int(0)
|
||||
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
import sys
|
||||
import io
|
||||
#import math
|
||||
#import urllib2
|
||||
#import urllib
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
@@ -138,7 +135,6 @@ class IssueIdentifier:
|
||||
try:
|
||||
cropped_im = im.crop((int(w / 2), 0, w, h))
|
||||
except Exception as e:
|
||||
sys.exc_clear()
|
||||
print("cropCover() error:", e)
|
||||
return None
|
||||
|
||||
@@ -438,8 +434,10 @@ class IssueIdentifier:
|
||||
|
||||
# assume that our search name is close to the actual name, say
|
||||
# within ,e.g. 5 chars
|
||||
shortened_key = utils.removearticles(keys['series'])
|
||||
shortened_item_name = utils.removearticles(item['name'])
|
||||
# sanitize both the search string and the result so that
|
||||
# we are comparing the same type of data
|
||||
shortened_key = utils.sanitize_title(keys['series'])
|
||||
shortened_item_name = utils.sanitize_title(item['name'])
|
||||
if len(shortened_item_name) < (
|
||||
len(shortened_key) + self.length_delta_thresh):
|
||||
length_approved = True
|
||||
|
||||
@@ -61,14 +61,7 @@ def ctmain():
|
||||
if opts.no_gui:
|
||||
cli.cli_mode(opts, SETTINGS)
|
||||
else:
|
||||
|
||||
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1'
|
||||
|
||||
#if platform.system() == "Darwin":
|
||||
# QtWidgets.QApplication.setStyle("macintosh")
|
||||
#else:
|
||||
# QtWidgets.QApplication.setStyle("Fusion")
|
||||
|
||||
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1'
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
if platform.system() == "Darwin":
|
||||
# Set the MacOS dock icon
|
||||
@@ -81,6 +74,11 @@ def ctmain():
|
||||
import ctypes
|
||||
myappid = u'comictagger' # arbitrary string
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
||||
# force close of console window
|
||||
SWP_HIDEWINDOW = 0x0080
|
||||
consoleWnd = ctypes.windll.kernel32.GetConsoleWindow()
|
||||
if consoleWnd != 0:
|
||||
ctypes.windll.user32.SetWindowPos(consoleWnd, None, 0, 0, 0, 0, SWP_HIDEWINDOW)
|
||||
|
||||
if platform.system() != "Linux":
|
||||
img = QtGui.QPixmap(ComicTaggerSettings.getGraphic('tags.png'))
|
||||
|
||||
@@ -25,6 +25,7 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from datetime import datetime
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .comicarchive import MetaDataStyle
|
||||
from .versionchecker import VersionChecker
|
||||
@@ -103,7 +104,7 @@ If no options are given, {0} will run in windowed mode.
|
||||
--version Display version.
|
||||
-h, --help Display this message.
|
||||
|
||||
For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
For more help visit the wiki at: https://github.com/comictagger/comictagger/wiki
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -341,7 +342,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.only_set_key = True
|
||||
if o == "--version":
|
||||
print((
|
||||
"ComicTagger {0}: Copyright (c) 2012-2014 Anthony Beville".format(ctversion.version)))
|
||||
"ComicTagger {}: Copyright (c) 2012-{:%Y} ComicTagger Team".format(ctversion.version, datetime.today())))
|
||||
print(
|
||||
"Distributed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)")
|
||||
sys.exit(0)
|
||||
|
||||
@@ -157,7 +157,7 @@ class PageListEditor(QWidget):
|
||||
self.modified.emit()
|
||||
|
||||
def changePageType(self, i):
|
||||
new_type = self.comboBox.itemData(i).toString()
|
||||
new_type = self.comboBox.itemData(i)
|
||||
if self.getCurrentPageType() != new_type:
|
||||
self.setCurrentPageType(new_type)
|
||||
self.emitFrontCoverChange()
|
||||
|
||||
@@ -76,7 +76,20 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
if md.isEmpty:
|
||||
md = ca.metadataFromFilename(self.settings.parse_scan_info)
|
||||
self.renamer.setMetadata(md)
|
||||
new_name = self.renamer.determineName(ca.path, ext=new_ext)
|
||||
self.renamer.move = self.settings.rename_move_dir
|
||||
|
||||
try:
|
||||
new_name = self.renamer.determineName(ca.path, ext=new_ext)
|
||||
except Exception as e:
|
||||
QtWidgets.QMessageBox.critical(self, 'Invalid format string!',
|
||||
'Your rename template is invalid!'
|
||||
'<br/><br/>{}<br/><br/>'
|
||||
'Please consult the template help in the '
|
||||
'settings and the documentation on the format at '
|
||||
'<a href=\'https://docs.python.org/3/library/string.html#format-string-syntax\'>'
|
||||
'https://docs.python.org/3/library/string.html#format-string-syntax</a>'.format(e))
|
||||
return
|
||||
|
||||
|
||||
row = self.twList.rowCount()
|
||||
self.twList.insertRow(row)
|
||||
@@ -149,17 +162,20 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
|
||||
if item['new_name'] == os.path.basename(item['archive'].path):
|
||||
folder = os.path.dirname(os.path.abspath(item['archive'].path))
|
||||
if self.settings.rename_move_dir and len(self.settings.rename_dir.strip()) > 3:
|
||||
folder = self.settings.rename_dir.strip()
|
||||
|
||||
new_abs_path = utils.unique_file(os.path.join(folder, item['new_name']))
|
||||
|
||||
if os.path.join(folder, item['new_name']) == item['archive'].path:
|
||||
print(item['new_name'], "Filename is already good!")
|
||||
continue
|
||||
|
||||
if not item['archive'].isWritable(check_rar_status=False):
|
||||
continue
|
||||
|
||||
folder = os.path.dirname(os.path.abspath(item['archive'].path))
|
||||
new_abs_path = utils.unique_file(
|
||||
os.path.join(folder, item['new_name']))
|
||||
|
||||
os.makedirs(os.path.dirname(new_abs_path), 0o777, True)
|
||||
os.rename(item['archive'].path, new_abs_path)
|
||||
|
||||
item['archive'].rename(new_abs_path)
|
||||
|
||||
@@ -36,14 +36,6 @@ class ComicTaggerSettings:
|
||||
if folder is not None:
|
||||
folder = folder
|
||||
return folder
|
||||
|
||||
@staticmethod
|
||||
def defaultLibunrarPath():
|
||||
return ComicTaggerSettings.baseDir() + "/libunrar.so"
|
||||
|
||||
@staticmethod
|
||||
def haveOwnUnrarLib():
|
||||
return os.path.exists(ComicTaggerSettings.defaultLibunrarPath())
|
||||
|
||||
@staticmethod
|
||||
def baseDir():
|
||||
@@ -66,7 +58,6 @@ class ComicTaggerSettings:
|
||||
def setDefaultValues(self):
|
||||
# General Settings
|
||||
self.rar_exe_path = ""
|
||||
self.unrar_lib_path = ""
|
||||
self.allow_cbi_in_rar = True
|
||||
self.check_for_new_version = False
|
||||
self.send_usage_stats = False
|
||||
@@ -94,7 +85,6 @@ class ComicTaggerSettings:
|
||||
self.show_disclaimer = True
|
||||
self.dont_notify_about_this_version = ""
|
||||
self.ask_about_usage_stats = True
|
||||
self.show_no_unrar_warning = True
|
||||
|
||||
# filename parsing settings
|
||||
self.parse_scan_info = True
|
||||
@@ -118,10 +108,12 @@ class ComicTaggerSettings:
|
||||
self.apply_cbl_transform_on_bulk_operation = False
|
||||
|
||||
# Rename settings
|
||||
self.rename_template = "%series% #%issue% (%year%)"
|
||||
self.rename_template = "{publisher}/{series}/{series} #{issue} - {title} ({year})"
|
||||
self.rename_issue_number_padding = 3
|
||||
self.rename_use_smart_string_cleanup = True
|
||||
self.rename_extension_based_on_archive = True
|
||||
self.rename_dir = ""
|
||||
self.rename_move_dir = False
|
||||
|
||||
# Auto-tag stickies
|
||||
self.save_on_low_confidence = False
|
||||
@@ -167,49 +159,7 @@ class ComicTaggerSettings:
|
||||
self.save()
|
||||
if self.rar_exe_path != "":
|
||||
# make sure rar program is now in the path for the rar class
|
||||
utils.addtopath(os.path.dirname(self.rar_exe_path))
|
||||
|
||||
if self.haveOwnUnrarLib():
|
||||
# We have a 'personal' copy of the unrar lib in the basedir, so
|
||||
# don't search and change the setting
|
||||
# NOTE: a manual edit of the settings file overrides this below
|
||||
os.environ["UNRAR_LIB_PATH"] = self.defaultLibunrarPath()
|
||||
|
||||
elif self.unrar_lib_path == "":
|
||||
# Priority is for unrar lib search is:
|
||||
# 1. explicit setting in settings file
|
||||
# 2. UNRAR_LIB_PATH in environment
|
||||
# 3. check some likely platform specific places
|
||||
if "UNRAR_LIB_PATH" in os.environ:
|
||||
self.unrar_lib_path = os.environ["UNRAR_LIB_PATH"]
|
||||
else:
|
||||
# look in some platform specific places:
|
||||
if platform.system() == "Windows":
|
||||
# Default location for the RARLab DLL installer
|
||||
if (platform.architecture()[0] == '64bit' and
|
||||
os.path.exists("C:\\Program Files (x86)\\UnrarDLL\\x64\\UnRAR64.dll")
|
||||
):
|
||||
self.unrar_lib_path = "C:\\Program Files (x86)\\UnrarDLL\\x64\\UnRAR64.dll"
|
||||
elif (platform.architecture()[0] == '32bit' and
|
||||
os.path.exists("C:\\Program Files\\UnrarDLL\\UnRAR.dll")
|
||||
):
|
||||
self.unrar_lib_path = "C:\\Program Files\\UnrarDLL\\UnRAR.dll"
|
||||
elif platform.system() == "Darwin":
|
||||
# Look for the brew unrar library
|
||||
if os.path.exists("/usr/local/lib/libunrar.dylib"):
|
||||
self.unrar_lib_path = "/usr/local/lib/libunrar.dylib"
|
||||
elif platform.system() == "Linux":
|
||||
if os.path.exists("/usr/local/lib/libunrar.so"):
|
||||
self.unrar_lib_path = "/usr/local/lib/libunrar.so"
|
||||
elif os.path.exists("/usr/lib/libunrar.so"):
|
||||
self.unrar_lib_path = "/usr/lib/libunrar.so"
|
||||
|
||||
if self.unrar_lib_path != "":
|
||||
self.save()
|
||||
|
||||
if self.unrar_lib_path != "":
|
||||
# This needs to occur before the unrar module is loaded for the first time
|
||||
os.environ["UNRAR_LIB_PATH"] = self.unrar_lib_path
|
||||
utils.addtopath(os.path.dirname(self.rar_exe_path))
|
||||
|
||||
def reset(self):
|
||||
os.unlink(self.settings_file)
|
||||
@@ -228,8 +178,6 @@ class ComicTaggerSettings:
|
||||
readline_generator(codecs.open(self.settings_file, "r", "utf8")))
|
||||
|
||||
self.rar_exe_path = self.config.get('settings', 'rar_exe_path')
|
||||
if self.config.has_option('settings', 'unrar_lib_path'):
|
||||
self.unrar_lib_path = self.config.get('settings', 'unrar_lib_path')
|
||||
if self.config.has_option('settings', 'check_for_new_version'):
|
||||
self.check_for_new_version = self.config.getboolean(
|
||||
'settings', 'check_for_new_version')
|
||||
@@ -297,9 +245,6 @@ class ComicTaggerSettings:
|
||||
if self.config.has_option('dialogflags', 'ask_about_usage_stats'):
|
||||
self.ask_about_usage_stats = self.config.getboolean(
|
||||
'dialogflags', 'ask_about_usage_stats')
|
||||
if self.config.has_option('dialogflags', 'show_no_unrar_warning'):
|
||||
self.show_no_unrar_warning = self.config.getboolean(
|
||||
'dialogflags', 'show_no_unrar_warning')
|
||||
|
||||
if self.config.has_option('comicvine', 'use_series_start_as_volume'):
|
||||
self.use_series_start_as_volume = self.config.getboolean(
|
||||
@@ -358,6 +303,10 @@ class ComicTaggerSettings:
|
||||
'rename', 'rename_extension_based_on_archive'):
|
||||
self.rename_extension_based_on_archive = self.config.getboolean(
|
||||
'rename', 'rename_extension_based_on_archive')
|
||||
if self.config.has_option('rename', 'rename_dir'):
|
||||
self.rename_dir = self.config.get('rename', 'rename_dir')
|
||||
if self.config.has_option('rename', 'rename_move_dir'):
|
||||
self.rename_move_dir = self.config.getboolean('rename', 'rename_move_dir')
|
||||
|
||||
if self.config.has_option('autotag', 'save_on_low_confidence'):
|
||||
self.save_on_low_confidence = self.config.getboolean(
|
||||
@@ -389,7 +338,6 @@ class ComicTaggerSettings:
|
||||
self.config.set(
|
||||
'settings', 'check_for_new_version', self.check_for_new_version)
|
||||
self.config.set('settings', 'rar_exe_path', self.rar_exe_path)
|
||||
self.config.set('settings', 'unrar_lib_path', self.unrar_lib_path)
|
||||
self.config.set('settings', 'send_usage_stats', self.send_usage_stats)
|
||||
|
||||
if not self.config.has_section('auto'):
|
||||
@@ -448,8 +396,6 @@ class ComicTaggerSettings:
|
||||
self.dont_notify_about_this_version)
|
||||
self.config.set(
|
||||
'dialogflags', 'ask_about_usage_stats', self.ask_about_usage_stats)
|
||||
self.config.set(
|
||||
'dialogflags', 'show_no_unrar_warning', self.show_no_unrar_warning)
|
||||
|
||||
if not self.config.has_section('filenameparser'):
|
||||
self.config.add_section('filenameparser')
|
||||
@@ -522,6 +468,8 @@ class ComicTaggerSettings:
|
||||
self.rename_use_smart_string_cleanup)
|
||||
self.config.set('rename', 'rename_extension_based_on_archive',
|
||||
self.rename_extension_based_on_archive)
|
||||
self.config.set('rename', 'rename_dir', self.rename_dir)
|
||||
self.config.set('rename', 'rename_move_dir', self.rename_move_dir)
|
||||
|
||||
if not self.config.has_section('autotag'):
|
||||
self.config.add_section('autotag')
|
||||
|
||||
@@ -24,6 +24,8 @@ from .settings import ComicTaggerSettings
|
||||
from .comicvinecacher import ComicVineCacher
|
||||
from .comicvinetalker import ComicVineTalker
|
||||
from .imagefetcher import ImageFetcher
|
||||
from .filerenamer import FileRenamer
|
||||
from .genericmetadata import GenericMetadata
|
||||
from . import utils
|
||||
|
||||
|
||||
@@ -53,33 +55,6 @@ macRarHelp = """
|
||||
</p>Once homebrew is installed, run: <b>brew install caskroom/cask/rar</b></body></html>
|
||||
"""
|
||||
|
||||
windowsUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need to have the unrar DLL from
|
||||
<span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://www.rarlab.com/rar_add.htm">
|
||||
RARLab</a></span> installed. </p></body></html>
|
||||
"""
|
||||
|
||||
linuxUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need to have the unrar library from RARLab installed.
|
||||
Look <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://github.com/beville/libunrar-binaries/releases">here</a></span>
|
||||
for pre-compiled binaries, or <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://www.rarlab.com/rar_add.htm">here</a></span>
|
||||
for the UnRAR source (which is easy to compile on Linux). </p></body></html>
|
||||
"""
|
||||
|
||||
macUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need the unrar library. The easiest way to get this is
|
||||
to install <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://brew.sh/homebrew">homebrew</a></span>.
|
||||
</p>Once homebrew is installed, run: <b>brew install unrar</b></body></html>
|
||||
"""
|
||||
|
||||
|
||||
class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, settings):
|
||||
@@ -91,29 +66,18 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
~QtCore.Qt.WindowContextHelpButtonHint)
|
||||
|
||||
self.settings = settings
|
||||
self.name = "Settings"
|
||||
|
||||
self.priorUnrarLibPath = self.settings.unrar_lib_path
|
||||
|
||||
if self.settings.haveOwnUnrarLib():
|
||||
# We have our own unrarlib, so no need for this GUI
|
||||
self.grpBoxUnrar.hide()
|
||||
self.name = "Settings"
|
||||
|
||||
if platform.system() == "Windows":
|
||||
self.lblRarHelp.setText(windowsRarHelp)
|
||||
self.lblUnrarHelp.setText(windowsUnrarHelp)
|
||||
|
||||
elif platform.system() == "Linux":
|
||||
self.lblRarHelp.setText(linuxRarHelp)
|
||||
self.lblUnrarHelp.setText(linuxUnrarHelp)
|
||||
|
||||
elif platform.system() == "Darwin":
|
||||
# Mac file dialog hides "/usr" and others, so allow user to type
|
||||
self.leUnrarLibPath.setReadOnly(False)
|
||||
self.leRarExePath.setReadOnly(False)
|
||||
|
||||
self.lblRarHelp.setText(macRarHelp)
|
||||
self.lblUnrarHelp.setText(macUnrarHelp)
|
||||
self.name = "Preferences"
|
||||
|
||||
self.setWindowTitle("ComicTagger " + self.name)
|
||||
@@ -148,16 +112,74 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.settingsToForm()
|
||||
|
||||
self.btnBrowseRar.clicked.connect(self.selectRar)
|
||||
self.btnBrowseUnrar.clicked.connect(self.selectUnrar)
|
||||
self.btnClearCache.clicked.connect(self.clearCache)
|
||||
self.btnResetSettings.clicked.connect(self.resetSettings)
|
||||
self.btnTestKey.clicked.connect(self.testAPIKey)
|
||||
self.btnTemplateHelp.clicked.connect(self.showTemplateHelp)
|
||||
|
||||
def configRenamer(self):
|
||||
md = GenericMetadata()
|
||||
md.isEmpty = False
|
||||
md.tagOrigin = "testing"
|
||||
|
||||
md.series = "series name"
|
||||
md.issue = "1"
|
||||
md.title = "issue title"
|
||||
md.publisher = "publisher"
|
||||
md.year = 1998
|
||||
md.month = 4
|
||||
md.day = 4
|
||||
md.issueCount = 1
|
||||
md.volume = 256
|
||||
md.genre = "test"
|
||||
md.language = "en" # 2 letter iso code
|
||||
md.comments = "This is definitly a comic." # use same way as Summary in CIX
|
||||
|
||||
md.volumeCount = 4096
|
||||
md.criticalRating = "Worst Comic Ever"
|
||||
md.country = "US"
|
||||
|
||||
md.alternateSeries = "None"
|
||||
md.alternateNumber = 4.4
|
||||
md.alternateCount = 4444
|
||||
md.imprint = 'imprint'
|
||||
md.notes = "This doesn't actually exist"
|
||||
md.webLink = "https://example.com/series name/1"
|
||||
md.format = "Box Set"
|
||||
md.manga = "Yes"
|
||||
md.blackAndWhite = False
|
||||
md.pageCount = 4
|
||||
md.maturityRating = "Everyone"
|
||||
|
||||
md.storyArc = "story"
|
||||
md.seriesGroup = "seriesGroup"
|
||||
md.scanInfo = "(lordwelch)"
|
||||
|
||||
md.characters = "character 1, character 2"
|
||||
md.teams = "None"
|
||||
md.locations = "Earth, 444 B.C."
|
||||
|
||||
md.credits = [dict({'role': 'Everything', 'person': 'author', 'primary': True})]
|
||||
md.tags = ["testing", "not real"]
|
||||
md.pages = [dict({'Image': '0', 'Type': 'Front Cover'}), dict({'Image': '1', 'Type': 'Story'})]
|
||||
|
||||
# Some CoMet-only items
|
||||
md.price = 0.00
|
||||
md.isVersionOf = "SERIES #1"
|
||||
md.rights = "None"
|
||||
md.identifier = "LW4444-Comic"
|
||||
md.lastMark = "0"
|
||||
md.coverImage = "https://example.com/series name/1/cover"
|
||||
|
||||
self.renamer = FileRenamer(md)
|
||||
self.renamer.setTemplate(str(self.leRenameTemplate.text()))
|
||||
self.renamer.setIssueZeroPadding(self.settings.rename_issue_number_padding)
|
||||
self.renamer.setSmartCleanup(self.settings.rename_use_smart_string_cleanup)
|
||||
|
||||
def settingsToForm(self):
|
||||
|
||||
# Copy values from settings to form
|
||||
self.leRarExePath.setText(self.settings.rar_exe_path)
|
||||
self.leUnrarLibPath.setText(self.settings.unrar_lib_path)
|
||||
self.leNameLengthDeltaThresh.setText(
|
||||
str(self.settings.id_length_delta_thresh))
|
||||
self.tePublisherBlacklist.setPlainText(
|
||||
@@ -205,23 +227,33 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.cbxSmartCleanup.setCheckState(QtCore.Qt.Checked)
|
||||
if self.settings.rename_extension_based_on_archive:
|
||||
self.cbxChangeExtension.setCheckState(QtCore.Qt.Checked)
|
||||
if self.settings.rename_move_dir:
|
||||
self.cbxMoveFiles.setCheckState(QtCore.Qt.Checked)
|
||||
self.leDirectory.setText(self.settings.rename_dir)
|
||||
|
||||
def accept(self):
|
||||
|
||||
self.configRenamer()
|
||||
|
||||
|
||||
try:
|
||||
new_name = self.renamer.determineName('test.cbz')
|
||||
except Exception as e:
|
||||
QtWidgets.QMessageBox.critical(self, 'Invalid format string!',
|
||||
'Your rename template is invalid!'
|
||||
'<br/><br/>{}<br/><br/>'
|
||||
'Please consult the template help in the '
|
||||
'settings and the documentation on the format at '
|
||||
'<a href=\'https://docs.python.org/3/library/string.html#format-string-syntax\'>'
|
||||
'https://docs.python.org/3/library/string.html#format-string-syntax</a>'.format(e))
|
||||
return
|
||||
|
||||
# Copy values from form to settings and save
|
||||
self.settings.rar_exe_path = str(self.leRarExePath.text())
|
||||
|
||||
# Don't accept the form info if we have our own unrar lib
|
||||
if not self.settings.haveOwnUnrarLib():
|
||||
self.settings.unrar_lib_path = str(self.leUnrarLibPath.text())
|
||||
|
||||
# make sure rar program is now in the path for the rar class
|
||||
if self.settings.rar_exe_path:
|
||||
utils.addtopath(os.path.dirname(self.settings.rar_exe_path))
|
||||
|
||||
if self.settings.unrar_lib_path:
|
||||
os.environ["UNRAR_LIB_PATH"] = self.settings.unrar_lib_path
|
||||
# This doesn't do anything... we need to restart!
|
||||
|
||||
if not str(self.leNameLengthDeltaThresh.text()).isdigit():
|
||||
self.leNameLengthDeltaThresh.setText("0")
|
||||
@@ -258,21 +290,15 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.leIssueNumPadding.text())
|
||||
self.settings.rename_use_smart_string_cleanup = self.cbxSmartCleanup.isChecked()
|
||||
self.settings.rename_extension_based_on_archive = self.cbxChangeExtension.isChecked()
|
||||
self.settings.rename_move_dir = self.cbxMoveFiles.isChecked()
|
||||
self.settings.rename_dir = self.leDirectory.text()
|
||||
|
||||
self.settings.save()
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
if self.priorUnrarLibPath != self.settings.unrar_lib_path:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "UnRar Library Change",
|
||||
"ComicTagger will need to be restarted for changes to take effect.")
|
||||
|
||||
def selectRar(self):
|
||||
self.selectFile(self.leRarExePath, "RAR")
|
||||
|
||||
def selectUnrar(self):
|
||||
self.selectFile(self.leUnrarLibPath, "UnRAR")
|
||||
|
||||
def clearCache(self):
|
||||
ImageFetcher().clearCache()
|
||||
ComicVineCacher().clearCache()
|
||||
@@ -324,3 +350,17 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
def showRenameTab(self):
|
||||
self.tabWidget.setCurrentIndex(5)
|
||||
|
||||
def showTemplateHelp(self):
|
||||
TemplateHelpWin = TemplateHelpWindow(self)
|
||||
TemplateHelpWin.setModal(False)
|
||||
TemplateHelpWin.show()
|
||||
|
||||
class TemplateHelpWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
super(TemplateHelpWindow, self).__init__(parent)
|
||||
|
||||
uic.loadUi(ComicTaggerSettings.getUIFile('TemplateHelp.ui'), self)
|
||||
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ from .cbltransformer import CBLTransformer
|
||||
from .renamewindow import RenameWindow
|
||||
from .exportwindow import ExportWindow, ExportConflictOpts
|
||||
from .issueidentifier import IssueIdentifier
|
||||
from .issuestring import IssueString
|
||||
from .autotagstartwindow import AutoTagStartWindow
|
||||
from .autotagprogresswindow import AutoTagProgressWindow
|
||||
from .autotagmatchwindow import AutoTagMatchWindow
|
||||
@@ -186,6 +187,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
validator = QtGui.QIntValidator(1900, 2099, self)
|
||||
self.lePubYear.setValidator(validator)
|
||||
|
||||
self.leSeriesPubYear.setValidator(validator)
|
||||
|
||||
validator = QtGui.QIntValidator(1, 12, self)
|
||||
self.lePubMonth.setValidator(validator)
|
||||
|
||||
@@ -761,14 +764,12 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
for child in widget.children():
|
||||
self.clearChildren(child)
|
||||
|
||||
# Copy all of the metadata object into to the form.
|
||||
# Merging of metadata should be done via the overlay function
|
||||
def metadataToForm(self):
|
||||
# copy the the metadata object into to the form
|
||||
|
||||
# helper func
|
||||
def assignText(field, value):
|
||||
if value is not None:
|
||||
field.setText(str(value))
|
||||
|
||||
md = self.metadata
|
||||
|
||||
assignText(self.leSeries, md.series)
|
||||
@@ -781,6 +782,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
assignText(self.lePubMonth, md.month)
|
||||
assignText(self.lePubYear, md.year)
|
||||
assignText(self.lePubDay, md.day)
|
||||
assignText(self.leSeriesPubYear, md.seriesYear)
|
||||
assignText(self.leGenre, md.genre)
|
||||
assignText(self.leImprint, md.imprint)
|
||||
assignText(self.teComments, md.comments)
|
||||
@@ -810,23 +812,33 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.cbMaturityRating.setEditText(md.maturityRating)
|
||||
else:
|
||||
self.cbMaturityRating.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbMaturityRating.setCurrentIndex(0)
|
||||
|
||||
if md.language is not None:
|
||||
i = self.cbLanguage.findData(md.language)
|
||||
self.cbLanguage.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbLanguage.setCurrentIndex(0)
|
||||
|
||||
if md.country is not None:
|
||||
i = self.cbCountry.findText(md.country)
|
||||
self.cbCountry.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbCountry.setCurrentIndex(0)
|
||||
|
||||
if md.manga is not None:
|
||||
i = self.cbManga.findData(md.manga)
|
||||
self.cbManga.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbManga.setCurrentIndex(0)
|
||||
|
||||
if md.blackAndWhite is not None and md.blackAndWhite:
|
||||
if md.blackAndWhite:
|
||||
self.cbBW.setChecked(True)
|
||||
else:
|
||||
self.cbBW.setChecked(False)
|
||||
|
||||
assignText(self.teTags, utils.listToString(md.tags))
|
||||
self.teTags.setText(utils.listToString(md.tags))
|
||||
|
||||
# !!! Should we clear the credits table or just avoid duplicates?
|
||||
while self.twCredits.rowCount() > 0:
|
||||
@@ -885,58 +897,48 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
return False
|
||||
|
||||
def formToMetadata(self):
|
||||
|
||||
# helper func
|
||||
def xlate(data, type_str):
|
||||
s = "{0}".format(data).strip()
|
||||
if s == "":
|
||||
return None
|
||||
elif type_str == "str":
|
||||
return s
|
||||
else:
|
||||
return int(s)
|
||||
|
||||
# copy the data from the form into the metadata
|
||||
md = self.metadata
|
||||
md.series = xlate(self.leSeries.text(), "str")
|
||||
md.issue = xlate(self.leIssueNum.text(), "str")
|
||||
md.issueCount = xlate(self.leIssueCount.text(), "int")
|
||||
md.volume = xlate(self.leVolumeNum.text(), "int")
|
||||
md.volumeCount = xlate(self.leVolumeCount.text(), "int")
|
||||
md.title = xlate(self.leTitle.text(), "str")
|
||||
md.publisher = xlate(self.lePublisher.text(), "str")
|
||||
md.month = xlate(self.lePubMonth.text(), "int")
|
||||
md.year = xlate(self.lePubYear.text(), "int")
|
||||
md.day = xlate(self.lePubDay.text(), "int")
|
||||
md.genre = xlate(self.leGenre.text(), "str")
|
||||
md.imprint = xlate(self.leImprint.text(), "str")
|
||||
md.comments = xlate(self.teComments.toPlainText(), "str")
|
||||
md.notes = xlate(self.teNotes.toPlainText(), "str")
|
||||
md.criticalRating = xlate(self.leCriticalRating.text(), "int")
|
||||
md.maturityRating = xlate(self.cbMaturityRating.currentText(), "str")
|
||||
md = GenericMetadata()
|
||||
md.isEmpty = False
|
||||
md.alternateNumber = IssueString(self.leAltIssueNum.text()).asString()
|
||||
md.issue = IssueString(self.leIssueNum.text()).asString()
|
||||
md.issueCount = utils.xlate(self.leIssueCount.text(), True)
|
||||
md.volume = utils.xlate(self.leVolumeNum.text(), True)
|
||||
md.volumeCount = utils.xlate(self.leVolumeCount.text(), True)
|
||||
md.month = utils.xlate(self.lePubMonth.text(), True)
|
||||
md.year = utils.xlate(self.lePubYear.text(), True)
|
||||
md.day = utils.xlate(self.lePubDay.text(), True)
|
||||
md.seriesYear = utils.xlate(self.leSeriesPubYear.text(), "int")
|
||||
md.criticalRating = utils.xlate(self.leCriticalRating.text(), True)
|
||||
md.alternateCount = utils.xlate(self.leAltIssueCount.text(), True)
|
||||
|
||||
md.storyArc = xlate(self.leStoryArc.text(), "str")
|
||||
md.scanInfo = xlate(self.leScanInfo.text(), "str")
|
||||
md.seriesGroup = xlate(self.leSeriesGroup.text(), "str")
|
||||
md.alternateSeries = xlate(self.leAltSeries.text(), "str")
|
||||
md.alternateNumber = xlate(self.leAltIssueNum.text(), "int")
|
||||
md.alternateCount = xlate(self.leAltIssueCount.text(), "int")
|
||||
md.webLink = xlate(self.leWebLink.text(), "str")
|
||||
md.characters = xlate(self.teCharacters.toPlainText(), "str")
|
||||
md.teams = xlate(self.teTeams.toPlainText(), "str")
|
||||
md.locations = xlate(self.teLocations.toPlainText(), "str")
|
||||
md.series = self.leSeries.text()
|
||||
md.title = self.leTitle.text()
|
||||
md.publisher = self.lePublisher.text()
|
||||
md.genre = self.leGenre.text()
|
||||
md.imprint = self.leImprint.text()
|
||||
md.comments = self.teComments.toPlainText()
|
||||
md.notes = self.teNotes.toPlainText()
|
||||
md.maturityRating = self.cbMaturityRating.currentText()
|
||||
|
||||
md.format = xlate(self.cbFormat.currentText(), "str")
|
||||
md.country = xlate(self.cbCountry.currentText(), "str")
|
||||
md.storyArc = self.leStoryArc.text()
|
||||
md.scanInfo = self.leScanInfo.text()
|
||||
md.seriesGroup = self.leSeriesGroup.text()
|
||||
md.alternateSeries = self.leAltSeries.text()
|
||||
md.webLink = self.leWebLink.text()
|
||||
md.characters = self.teCharacters.toPlainText()
|
||||
md.teams = self.teTeams.toPlainText()
|
||||
md.locations = self.teLocations.toPlainText()
|
||||
|
||||
langiso = self.cbLanguage.itemData(self.cbLanguage.currentIndex())
|
||||
md.language = xlate(langiso, "str")
|
||||
md.format = self.cbFormat.currentText()
|
||||
md.country = self.cbCountry.currentText()
|
||||
|
||||
manga_code = self.cbManga.itemData(self.cbManga.currentIndex())
|
||||
md.manga = xlate(manga_code, "str")
|
||||
md.language = utils.xlate(self.cbLanguage.itemData(self.cbLanguage.currentIndex()))
|
||||
|
||||
md.manga = utils.xlate(self.cbManga.itemData(self.cbManga.currentIndex()))
|
||||
|
||||
# Make a list from the coma delimited tags string
|
||||
tmp = xlate(self.teTags.toPlainText(), "str")
|
||||
tmp = self.teTags.toPlainText()
|
||||
if tmp is not None:
|
||||
def striplist(l):
|
||||
return([x.strip() for x in l])
|
||||
@@ -961,6 +963,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
md.pages = self.pageListEditor.getPageList()
|
||||
|
||||
self.metadata = md
|
||||
|
||||
def useFilename(self):
|
||||
if self.comic_archive is not None:
|
||||
# copy the form onto metadata object
|
||||
@@ -984,13 +988,9 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
if self.settings.last_opened_folder is not None:
|
||||
dialog.setDirectory(self.settings.last_opened_folder)
|
||||
# dialog.setFileMode(QtWidgets.QFileDialog.Directory)
|
||||
|
||||
if not folder_mode:
|
||||
if platform.system() != "Windows" and utils.which("unrar") is None:
|
||||
archive_filter = "Comic archive files (*.cbz *.zip)"
|
||||
else:
|
||||
archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)"
|
||||
archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)"
|
||||
filters = [
|
||||
archive_filter,
|
||||
"Any files (*)"
|
||||
@@ -2107,7 +2107,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
def versionCheckComplete(self, new_version):
|
||||
if (new_version != self.version and
|
||||
new_version != self.settings.dont_notify_about_this_version):
|
||||
website = "http://code.google.com/p/comictagger"
|
||||
website = "https://github.com/comictagger/comictagger"
|
||||
checked = OptionalMessageDialog.msg(
|
||||
self,
|
||||
"New version available!",
|
||||
|
||||
107
comictaggerlib/ui/TemplateHelp.ui
Normal file
107
comictaggerlib/ui/TemplateHelp.ui
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>702</width>
|
||||
<height>452</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Template Help</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="html">
|
||||
<string><html>
|
||||
<head/>
|
||||
<body>
|
||||
<h1 style="text-align: center">Template help</h1>
|
||||
<p>The template uses Python format strings, in the simplest use it replaces the field (e.g. {issue}) with the value for that particular comic (e.g. 1) for advanced formatting please reference the
|
||||
|
||||
<a href="https://docs.python.org/3/library/string.html#format-string-syntax">Python 3 documentation</a></p>
|
||||
<pre>Accepts the following variables:
|
||||
{isEmpty}		(boolean)
|
||||
{tagOrigin}		(string)
|
||||
{series}		(string)
|
||||
{issue}		(string)
|
||||
{title}		(string)
|
||||
{publisher}		(string)
|
||||
{month}		(integer)
|
||||
{year}		(integer)
|
||||
{day}		(integer)
|
||||
{issueCount}	(integer)
|
||||
{volume}		(integer)
|
||||
{genre}		(string)
|
||||
{language}		(string)
|
||||
{comments}		(string)
|
||||
{volumeCount}	(integer)
|
||||
{criticalRating}	(string)
|
||||
{country}		(string)
|
||||
{alternateSeries}	(string)
|
||||
{alternateNumber}	(string)
|
||||
{alternateCount}	(integer)
|
||||
{imprint}		(string)
|
||||
{notes}		(string)
|
||||
{webLink}		(string)
|
||||
{format}		(string)
|
||||
{manga}		(string)
|
||||
{blackAndWhite}	(boolean)
|
||||
{pageCount}		(integer)
|
||||
{maturityRating}	(string)
|
||||
{storyArc}		(string)
|
||||
{seriesGroup}	(string)
|
||||
{scanInfo}		(string)
|
||||
{characters}	(string)
|
||||
{teams}		(string)
|
||||
{locations}		(string)
|
||||
{credits}		(list of dict({'role': 'str', 'person': 'str', 'primary': boolean}))
|
||||
{tags}		(list of str)
|
||||
{pages}		(list of dict({'Image': 'str(int)', 'Type': 'str'}))
|
||||
|
||||
CoMet-only items:
|
||||
{price}		(float)
|
||||
{isVersionOf}	(string)
|
||||
{rights}		(string)
|
||||
{identifier}	(string)
|
||||
{lastMark}	(string)
|
||||
{coverImage}	(string)
|
||||
|
||||
Examples:
|
||||
|
||||
{series} {issue} ({year})
|
||||
Spider-Geddon 1 (2018)
|
||||
|
||||
{series} #{issue} - {title}
|
||||
Spider-Geddon #1 - New Players; Check In
|
||||
|
||||
</pre>
|
||||
</body>
|
||||
</html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -359,7 +359,7 @@
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnTestKey">
|
||||
<property name="text">
|
||||
<string>Tesk Key</string>
|
||||
<string>Test Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -503,7 +503,7 @@
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="lblTemplate">
|
||||
<property name="text">
|
||||
<string>Template:</string>
|
||||
</property>
|
||||
@@ -512,31 +512,80 @@
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leRenameTemplate">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The template for the new filename. Accepts the following variables:</p><p>%series%<br/>%issue%<br/>%volume%<br/>%issuecount%<br/>%year%<br/>%month%<br/>%month_name%<br/>%publisher%<br/>%title%<br/>
|
||||
%genre%<br/>
|
||||
%language_code%<br/>
|
||||
%criticalrating%<br/>
|
||||
%alternateseries%<br/>
|
||||
%alternatenumber%<br/>
|
||||
%alternatecount%<br/>
|
||||
%imprint%<br/>
|
||||
%format%<br/>
|
||||
%maturityrating%<br/>
|
||||
%storyarc%<br/>
|
||||
%seriesgroup%<br/>
|
||||
%scaninfo%
|
||||
</p><p>Examples:</p><p><span style=" font-style:italic;">%series% %issue% (%year%)</span><br/><span style=" font-style:italic;">%series% #%issue% - %title%</span></p></body></html></string>
|
||||
<string><pre>The template for the new filename. Uses python format strings https://docs.python.org/3/library/string.html#format-string-syntax
|
||||
Accepts the following variables:
|
||||
{isEmpty} (boolean)
|
||||
{tagOrigin} (string)
|
||||
{series} (string)
|
||||
{issue} (string)
|
||||
{title} (string)
|
||||
{publisher} (string)
|
||||
{month} (integer)
|
||||
{year} (integer)
|
||||
{day} (integer)
|
||||
{issueCount} (integer)
|
||||
{volume} (integer)
|
||||
{genre} (string)
|
||||
{language} (string)
|
||||
{comments} (string)
|
||||
{volumeCount} (integer)
|
||||
{criticalRating} (string)
|
||||
{country} (string)
|
||||
{alternateSeries} (string)
|
||||
{alternateNumber} (string)
|
||||
{alternateCount} (integer)
|
||||
{imprint} (string)
|
||||
{notes} (string)
|
||||
{webLink} (string)
|
||||
{format} (string)
|
||||
{manga} (string)
|
||||
{blackAndWhite} (boolean)
|
||||
{pageCount} (integer)
|
||||
{maturityRating} (string)
|
||||
{storyArc} (string)
|
||||
{seriesGroup} (string)
|
||||
{scanInfo} (string)
|
||||
{characters} (string)
|
||||
{teams} (string)
|
||||
{locations} (string)
|
||||
{credits} (list of dict({&apos;role&apos;: &apos;str&apos;, &apos;person&apos;: &apos;str&apos;, &apos;primary&apos;: boolean}))
|
||||
{tags} (list of str)
|
||||
{pages} (list of dict({&apos;Image&apos;: &apos;str(int)&apos;, &apos;Type&apos;: &apos;str&apos;}))
|
||||
|
||||
CoMet-only items:
|
||||
{price} (float)
|
||||
{isVersionOf} (string)
|
||||
{rights} (string)
|
||||
{identifier} (string)
|
||||
{lastMark} (string)
|
||||
{coverImage} (string)
|
||||
|
||||
Examples:
|
||||
|
||||
{series} {issue} ({year})
|
||||
Spider-Geddon 1 (2018)
|
||||
|
||||
{series} #{issue} - {title}
|
||||
Spider-Geddon #1 - New Players; Check In
|
||||
</pre></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<widget class="QPushButton" name="btnTemplateHelp">
|
||||
<property name="text">
|
||||
<string>Template Help</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="lblPadding">
|
||||
<property name="text">
|
||||
<string>Issue # Zero Padding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="leIssueNumPadding">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
@@ -555,7 +604,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxSmartCleanup">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">&quot;Smart Text Cleanup&quot; </span>will attempt to clean up the new filename if there are missing fields from the template. For example, removing empty braces, repeated spaces and dashes, and more. Experimental feature.</p></body></html></string>
|
||||
@@ -565,13 +614,33 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxChangeExtension">
|
||||
<property name="text">
|
||||
<string>Change Extension Based On Archive Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="cbxMoveFiles">
|
||||
<property name="toolTip">
|
||||
<string>If checked moves files to specified folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move files when renaming</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="lblDirectory">
|
||||
<property name="text">
|
||||
<string>Destination Directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="leDirectory"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -581,67 +650,6 @@
|
||||
<string>RAR Tools</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxUnrar">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lblUnrar">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>UnRAR library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leUnrarLibPath">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnBrowseUnrar">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblUnrarHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>In order to read CBR/RAR archives, you will need to have the unrar library from <a href="www.win-rar.com/download.html"><span style=" text-decoration: underline; color:#0000ff;">WinRAR</span></a> installed. </p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxRar">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
@@ -699,7 +707,7 @@
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblRarHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
|
||||
@@ -512,6 +512,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLineEdit" name="leSeriesPubYear"/>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Series Year</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
import sys
|
||||
import platform
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import requests
|
||||
import urllib.parse
|
||||
#import os
|
||||
#import urllib
|
||||
|
||||
try:
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
@@ -47,28 +47,30 @@ class VersionChecker(QObject):
|
||||
|
||||
base_url = "http://comictagger1.appspot.com/latest"
|
||||
args = ""
|
||||
|
||||
params = dict()
|
||||
if use_stats:
|
||||
params = {
|
||||
'uuid': uuid,
|
||||
'version': ctversion.version
|
||||
}
|
||||
if platform.system() == "Windows":
|
||||
plat = "win"
|
||||
params['platform'] = "win"
|
||||
elif platform.system() == "Linux":
|
||||
plat = "lin"
|
||||
params['platform'] = "lin"
|
||||
elif platform.system() == "Darwin":
|
||||
plat = "mac"
|
||||
params['platform'] = "mac"
|
||||
else:
|
||||
plat = "other"
|
||||
args = "?uuid={0}&platform={1}&version={2}".format(
|
||||
uuid, plat, ctversion.version)
|
||||
if not getattr(sys, 'frozen', None):
|
||||
args += "&src=T"
|
||||
params['platform'] = "other"
|
||||
|
||||
return base_url + args
|
||||
if not getattr(sys, 'frozen', None):
|
||||
params['src'] = 'T'
|
||||
|
||||
return (base_url, params)
|
||||
|
||||
def getLatestVersion(self, uuid, use_stats=True):
|
||||
|
||||
try:
|
||||
resp = urllib.request.urlopen(self.getRequestUrl(uuid, use_stats))
|
||||
new_version = resp.read()
|
||||
url, params = self.getRequestUrl(uuid, use_stats)
|
||||
new_version = requests.get(url, params=params).text
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
@@ -79,12 +81,11 @@ class VersionChecker(QObject):
|
||||
versionRequestComplete = pyqtSignal(str)
|
||||
|
||||
def asyncGetLatestVersion(self, uuid, use_stats):
|
||||
|
||||
url = self.getRequestUrl(uuid, use_stats)
|
||||
url, params = self.getRequestUrl(uuid, use_stats)
|
||||
|
||||
self.nam = QNetworkAccessManager()
|
||||
self.nam.finished.connect(self.asyncGetLatestVersionComplete)
|
||||
self.nam.get(QNetworkRequest(QUrl(str(url))))
|
||||
self.nam.get(QNetworkRequest(QUrl(str(url + '?' + urllib.parse.urlencode(params)))))
|
||||
|
||||
def asyncGetLatestVersionComplete(self, reply):
|
||||
if (reply.error() != QNetworkReply.NoError):
|
||||
|
||||
@@ -5,7 +5,7 @@ TAGGER_BASE ?= ../
|
||||
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
|
||||
|
||||
APP_NAME := ComicTagger
|
||||
VERSION_STR := $(shell python setup.py --version)
|
||||
VERSION_STR := $(shell cd .. && python setup.py --version)
|
||||
|
||||
MAC_BASE := $(TAGGER_BASE)/mac
|
||||
DIST_DIR := $(MAC_BASE)/dist
|
||||
@@ -21,7 +21,6 @@ dist:
|
||||
$(PYINSTALLER_CMD) $(TAGGER_BASE)/comictagger.py -w -n $(APP_NAME) -s
|
||||
cp -a $(TAGGER_SRC)/ui $(APP_BUNDLE)/Contents/MacOS
|
||||
cp -a $(TAGGER_SRC)/graphics $(APP_BUNDLE)/Contents/MacOS
|
||||
cp $(MAC_BASE)/libunrar.so $(APP_BUNDLE)/Contents/MacOS
|
||||
cp $(MAC_BASE)/app.icns $(APP_BUNDLE)/Contents/Resources/icon-windowed.icns
|
||||
# fix the version string in the Info.plist
|
||||
sed -i -e 's/0\.0\.0/$(VERSION_STR)/' $(MAC_BASE)/dist/ComicTagger.app/Contents/Info.plist
|
||||
|
||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[tool.black]
|
||||
line-length = 150
|
||||
|
||||
[tool.isort]
|
||||
line_length = 150
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools_scm]
|
||||
write_to = "comictaggerlib/ctversion.py"
|
||||
local_scheme = "no-local-version"
|
||||
1
requirements-CBR.txt
Normal file
1
requirements-CBR.txt
Normal file
@@ -0,0 +1 @@
|
||||
unrar-cffi>=0.2.2
|
||||
1
requirements-GUI.txt
Normal file
1
requirements-GUI.txt
Normal file
@@ -0,0 +1 @@
|
||||
PyQt5<=5.15.3
|
||||
@@ -1,8 +1,7 @@
|
||||
configparser
|
||||
beautifulsoup4 >= 4.1
|
||||
unrar==0.3
|
||||
natsort==3.5.2
|
||||
PyPDF2==1.24
|
||||
configparser
|
||||
natsort
|
||||
pillow>=4.3.0
|
||||
PyQt5>=5.10.1
|
||||
pyinstaller>=3.5
|
||||
requests
|
||||
pathvalidate
|
||||
|
||||
4
requirements_dev.txt
Normal file
4
requirements_dev.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
pyinstaller==4.3
|
||||
setuptools>=42
|
||||
setuptools_scm[toml]>=3.4
|
||||
wheel
|
||||
289
setup.py
289
setup.py
@@ -1,244 +1,81 @@
|
||||
# Setup file for comictagger python source (no wheels yet)
|
||||
#
|
||||
# The install process will attempt to compile the unrar lib from source.
|
||||
# If it succeeds, the unrar lib binary will be installed with the python
|
||||
# source. If it fails, install will just continue. On most Linux systems it
|
||||
# should just work. (Tested on a Mac system with homebrew, as well)
|
||||
#
|
||||
# An entry point script called "comictagger" will be created
|
||||
#
|
||||
# Currently commented out, an experiment at desktop integration.
|
||||
# It seems that post installation tweaks are broken by wheel files.
|
||||
# Kept here for further research
|
||||
|
||||
from __future__ import print_function
|
||||
from setuptools import setup
|
||||
from setuptools import dist
|
||||
from setuptools import Command
|
||||
import setuptools.command.build_py
|
||||
import setuptools.command.install
|
||||
import subprocess
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import platform
|
||||
import tempfile
|
||||
|
||||
python_requires='>=3',
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
with open('requirements.txt') as f:
|
||||
required = f.read().splitlines()
|
||||
# Always require PyQt5 on Windows and Mac
|
||||
if platform.system() in [ "Windows", "Darwin" ]:
|
||||
required.append("PyQt5")
|
||||
def read(fname):
|
||||
"""
|
||||
Read the contents of a file.
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Path to file.
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
File contents.
|
||||
"""
|
||||
with open(os.path.join(os.path.dirname(__file__), fname)) as f:
|
||||
return f.read()
|
||||
|
||||
platform_data_files = []
|
||||
|
||||
"""
|
||||
if platform.system() in [ "Windows" ]:
|
||||
required.append("winshell")
|
||||
install_requires = read("requirements.txt").splitlines()
|
||||
|
||||
# Some files to install on different platforms
|
||||
# Dynamically determine extra dependencies
|
||||
extras_require = {}
|
||||
extra_req_files = glob.glob("requirements-*.txt")
|
||||
for extra_req_file in extra_req_files:
|
||||
name = os.path.splitext(extra_req_file)[0].replace("requirements-", "", 1)
|
||||
extras_require[name] = read(extra_req_file).splitlines()
|
||||
|
||||
if platform.system() == "Linux":
|
||||
linux_desktop_shortcut = "/usr/local/share/applications/ComicTagger.desktop"
|
||||
platform_data_files = [("/usr/local/share/applications",
|
||||
["desktop-integration/linux/ComicTagger.desktop"]),
|
||||
("/usr/local/share/comictagger",
|
||||
["comictaggerlib/graphics/app.png"]),
|
||||
]
|
||||
|
||||
if platform.system() == "Windows":
|
||||
win_desktop_folder = os.path.join(os.environ["USERPROFILE"], "Desktop")
|
||||
win_appdata_folder = os.path.join(os.environ["APPDATA"], "comictagger")
|
||||
win_desktop_shortcut = os.path.join(win_desktop_folder, "ComicTagger-pip.lnk")
|
||||
platform_data_files = [(win_desktop_folder,
|
||||
["desktop-integration/windows/ComicTagger-pip.lnk"]),
|
||||
(win_appdata_folder,
|
||||
["windows/app.ico"]),
|
||||
]
|
||||
# If there are any extras, add a catch-all case that includes everything.
|
||||
# This assumes that entries in extras_require are lists (not single strings),
|
||||
# and that there are no duplicated packages across the extras.
|
||||
if extras_require:
|
||||
extras_require["all"] = sorted({x for v in extras_require.values() for x in v})
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
mac_app_folder = "/Applications"
|
||||
ct_app_name = "ComicTagger-pip.app"
|
||||
mac_app_infoplist = os.path.join(mac_app_folder, ct_app_name, "Contents", "Info.plist")
|
||||
mac_app_main = os.path.join(mac_app_folder, ct_app_name, "MacOS", "main.sh")
|
||||
mac_python_link = os.path.join(mac_app_folder, ct_app_name, "MacOS", "ComicTagger")
|
||||
platform_data_files = [(os.path.join(mac_app_folder, ct_app_name, "Contents"),
|
||||
["desktop-integration/mac/Info.plist"]),
|
||||
(os.path.join(mac_app_folder, ct_app_name, "Contents/Resources"),
|
||||
["mac/app.icns"]),
|
||||
(os.path.join(mac_app_folder, ct_app_name, "Contents/MacOS"),
|
||||
["desktop-integration/mac/main.sh",
|
||||
"desktop-integration/mac/ComicTagger"]),
|
||||
]
|
||||
|
||||
def fileTokenReplace(filename, token, replacement):
|
||||
with open(filename, "rt") as fin:
|
||||
fd, tmpfile = tempfile.mkstemp()
|
||||
with open(tmpfile, "wt") as fout:
|
||||
for line in fin:
|
||||
fout.write(line.replace('%%{}%%'.format(token), replacement))
|
||||
os.close(fd)
|
||||
# fix permissions of temp file
|
||||
os.chmod(tmpfile, 420) #Octal 0o644
|
||||
os.rename(tmpfile, filename)
|
||||
|
||||
def postInstall(scripts_folder):
|
||||
entry_point_script = os.path.join(scripts_folder, "comictagger")
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# doctor the shortcut for this windows system after deployment
|
||||
import winshell
|
||||
winshell.CreateShortcut(
|
||||
Path=os.path.abspath(win_desktop_shortcut),
|
||||
Target=entry_point_script + ".exe",
|
||||
Icon=(os.path.join(win_appdata_folder, 'app.ico'), 0),
|
||||
Description="Launch ComicTagger as installed by PIP"
|
||||
)
|
||||
|
||||
if platform.system() == "Linux":
|
||||
# doctor the script path in the desktop file
|
||||
fileTokenReplace(linux_desktop_shortcut,
|
||||
"CTSCRIPT",
|
||||
entry_point_script)
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
# doctor the plist app version
|
||||
fileTokenReplace(mac_app_infoplist,
|
||||
"CTVERSION",
|
||||
comictaggerlib.ctversion.version)
|
||||
# doctor the script path in main.sh
|
||||
fileTokenReplace(mac_app_main,
|
||||
"CTSCRIPT",
|
||||
entry_point_script)
|
||||
# Make the launcher script executable
|
||||
os.chmod(mac_app_main, 509) #Octal 0o775
|
||||
|
||||
# Final install step: create a symlink to Python OS X application
|
||||
punt = False
|
||||
pythonpath,top = os.path.split(os.path.realpath(sys.executable))
|
||||
while top:
|
||||
if 'Resources' in pythonpath:
|
||||
pass
|
||||
elif os.path.exists(os.path.join(pythonpath,'Resources')):
|
||||
break
|
||||
pythonpath,top = os.path.split(pythonpath)
|
||||
else:
|
||||
print("Failed to find a Resources directory associated with ", str(sys.executable))
|
||||
punt = True
|
||||
|
||||
if not punt:
|
||||
pythonapp = os.path.join(pythonpath, 'Resources','Python.app','Contents','MacOS','Python')
|
||||
if not os.path.exists(pythonapp):
|
||||
print("Failed to find a Python app in ", str(pythonapp))
|
||||
punt = True
|
||||
|
||||
# remove the placeholder
|
||||
os.remove(mac_python_link)
|
||||
if not punt:
|
||||
os.symlink(pythonapp, mac_python_link)
|
||||
else:
|
||||
# We failed, but we can still be functional
|
||||
os.symlink(sys.executable, mac_python_link)
|
||||
"""
|
||||
|
||||
class BuildUnrarCommand(Command):
|
||||
description = 'build unrar library'
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if not os.path.exists("comictaggerlib/libunrar.so"):
|
||||
if not os.path.exists("unrar/libunrar.so"):
|
||||
print("Building C++ unrar library....")
|
||||
subprocess.call(['make', '-C', 'unrar', 'lib'])
|
||||
print("Copying .so file to comictaggerlib folder")
|
||||
shutil.copyfile("unrar/libunrar.so", "comictaggerlib/libunrar.so")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("WARNING ---- Unrar library build/deploy failed. User will have to self-install libunrar.")
|
||||
|
||||
|
||||
class BuildPyCommand(setuptools.command.build_py.build_py):
|
||||
"""Custom build command."""
|
||||
|
||||
def run(self):
|
||||
self.run_command('build_unrar')
|
||||
setuptools.command.build_py.build_py.run(self)
|
||||
|
||||
class customInstall(setuptools.command.install.install):
|
||||
"""Custom install command."""
|
||||
|
||||
def run(self):
|
||||
|
||||
# Do the standard install
|
||||
setuptools.command.install.install.run(self)
|
||||
|
||||
# Custom post install
|
||||
#postInstall(self.install_scripts)
|
||||
|
||||
#----------------------------------------------------
|
||||
setup(name="comictagger",
|
||||
install_requires=required,
|
||||
cmdclass={
|
||||
'build_unrar': BuildUnrarCommand,
|
||||
'build_py': BuildPyCommand,
|
||||
'install': customInstall,
|
||||
},
|
||||
description="A cross-platform GUI/CLI app for writing metadata to comic archives",
|
||||
author="ComicTagger team",
|
||||
author_email="comictagger@gmail.com",
|
||||
url="https://github.com/comictagger/comictagger",
|
||||
packages=["comictaggerlib", "comicapi"],
|
||||
package_data={
|
||||
'comictaggerlib': ['ui/*', 'graphics/*', '*.so'],
|
||||
},
|
||||
entry_points=dict(console_scripts=['comictagger=comictaggerlib.main:ctmain']),
|
||||
data_files=platform_data_files,
|
||||
setup_requires=[
|
||||
"setuptools_scm"
|
||||
],
|
||||
use_scm_version={
|
||||
'write_to': 'comictaggerlib/ctversion.py'
|
||||
},
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Other/Nonlisted Topic",
|
||||
"Topic :: Multimedia :: Graphics"
|
||||
],
|
||||
keywords=['comictagger', 'comics', 'comic', 'metadata', 'tagging', 'tagger'],
|
||||
license="Apache License 2.0",
|
||||
|
||||
long_description="""
|
||||
ComicTagger is a multi-platform app for writing metadata to digital comics, written in Python and PyQt.
|
||||
|
||||
Features:
|
||||
|
||||
* Runs on Mac OSX, Microsoft Windows, and Linux systems
|
||||
* Communicates with an online database (Comic Vine) for acquiring metadata
|
||||
* Uses image processing to automatically match a given archive with the correct issue data
|
||||
* Batch processing in the GUI for tagging hundreds or more comics at a time
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover and ComicRack).
|
||||
* Reads and writes RAR and Zip archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be used in native scripts for complex operations.
|
||||
* Can run without PyQt5 installed
|
||||
"""
|
||||
)
|
||||
setup(
|
||||
name="comictagger",
|
||||
install_requires=install_requires,
|
||||
extras_require=extras_require,
|
||||
python_requires=">=3",
|
||||
description="A cross-platform GUI/CLI app for writing metadata to comic archives",
|
||||
author="ComicTagger team",
|
||||
author_email="comictagger@gmail.com",
|
||||
url="https://github.com/comictagger/comictagger",
|
||||
packages=["comictaggerlib", "comicapi"],
|
||||
package_data={
|
||||
"comictaggerlib": ["ui/*", "graphics/*"],
|
||||
},
|
||||
entry_points=dict(console_scripts=["comictagger=comictaggerlib.main:ctmain"]),
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Other/Nonlisted Topic",
|
||||
"Topic :: Multimedia :: Graphics",
|
||||
],
|
||||
keywords=["comictagger", "comics", "comic", "metadata", "tagging", "tagger"],
|
||||
license="Apache License 2.0",
|
||||
long_description=read("README.md"),
|
||||
long_description_content_type='text/markdown'
|
||||
)
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{95CC809B-03FC-4EDB-BB20-FD07A698C05F}</ProjectGuid>
|
||||
<RootNamespace>UnRAR</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>build\unrar32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>build\unrar64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>build\unrar32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>build\unrar64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>false</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="archive.cpp" />
|
||||
<ClCompile Include="arcread.cpp" />
|
||||
<ClCompile Include="blake2s.cpp" />
|
||||
<ClCompile Include="cmddata.cpp" />
|
||||
<ClCompile Include="consio.cpp" />
|
||||
<ClCompile Include="crc.cpp" />
|
||||
<ClCompile Include="crypt.cpp" />
|
||||
<ClCompile Include="encname.cpp" />
|
||||
<ClCompile Include="errhnd.cpp" />
|
||||
<ClCompile Include="extinfo.cpp" />
|
||||
<ClCompile Include="extract.cpp" />
|
||||
<ClCompile Include="filcreat.cpp" />
|
||||
<ClCompile Include="file.cpp" />
|
||||
<ClCompile Include="filefn.cpp" />
|
||||
<ClCompile Include="filestr.cpp" />
|
||||
<ClCompile Include="find.cpp" />
|
||||
<ClCompile Include="getbits.cpp" />
|
||||
<ClCompile Include="global.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hash.cpp" />
|
||||
<ClCompile Include="headers.cpp" />
|
||||
<ClCompile Include="isnt.cpp" />
|
||||
<ClCompile Include="list.cpp" />
|
||||
<ClCompile Include="match.cpp" />
|
||||
<ClCompile Include="options.cpp" />
|
||||
<ClCompile Include="pathfn.cpp" />
|
||||
<ClCompile Include="qopen.cpp" />
|
||||
<ClCompile Include="rar.cpp" />
|
||||
<ClCompile Include="rarpch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rarvm.cpp" />
|
||||
<ClCompile Include="rawread.cpp" />
|
||||
<ClCompile Include="rdwrfn.cpp" />
|
||||
<ClCompile Include="recvol.cpp" />
|
||||
<ClCompile Include="resource.cpp" />
|
||||
<ClCompile Include="rijndael.cpp" />
|
||||
<ClCompile Include="rs.cpp" />
|
||||
<ClCompile Include="rs16.cpp" />
|
||||
<ClCompile Include="scantree.cpp" />
|
||||
<ClCompile Include="secpassword.cpp" />
|
||||
<ClCompile Include="sha1.cpp" />
|
||||
<ClCompile Include="sha256.cpp" />
|
||||
<ClCompile Include="smallfn.cpp" />
|
||||
<ClCompile Include="strfn.cpp" />
|
||||
<ClCompile Include="strlist.cpp" />
|
||||
<ClCompile Include="system.cpp" />
|
||||
<ClCompile Include="threadpool.cpp" />
|
||||
<ClCompile Include="timefn.cpp" />
|
||||
<ClCompile Include="ui.cpp" />
|
||||
<ClCompile Include="unicode.cpp" />
|
||||
<ClCompile Include="unpack.cpp" />
|
||||
<ClCompile Include="volume.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,420 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="release_nocrypt|Win32">
|
||||
<Configuration>release_nocrypt</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="release_nocrypt|x64">
|
||||
<Configuration>release_nocrypt</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>UnRAR</ProjectName>
|
||||
<ProjectGuid>{E815C46C-36C4-499F-BBC2-E772C6B17971}</ProjectGuid>
|
||||
<RootNamespace>UnRAR</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
|
||||
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
|
||||
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<StructMemberAlignment>4Bytes</StructMemberAlignment>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<StructMemberAlignment>4Bytes</StructMemberAlignment>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>4Bytes</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>false</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>4Bytes</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>4Bytes</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>false</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>4Bytes</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="archive.cpp" />
|
||||
<ClCompile Include="arcread.cpp" />
|
||||
<ClCompile Include="blake2s.cpp" />
|
||||
<ClCompile Include="cmddata.cpp" />
|
||||
<ClCompile Include="consio.cpp" />
|
||||
<ClCompile Include="crc.cpp" />
|
||||
<ClCompile Include="crypt.cpp" />
|
||||
<ClCompile Include="dll.cpp" />
|
||||
<ClCompile Include="encname.cpp" />
|
||||
<ClCompile Include="errhnd.cpp" />
|
||||
<ClCompile Include="extinfo.cpp" />
|
||||
<ClCompile Include="extract.cpp" />
|
||||
<ClCompile Include="filcreat.cpp" />
|
||||
<ClCompile Include="file.cpp" />
|
||||
<ClCompile Include="filefn.cpp" />
|
||||
<ClCompile Include="filestr.cpp" />
|
||||
<ClCompile Include="find.cpp" />
|
||||
<ClCompile Include="getbits.cpp" />
|
||||
<ClCompile Include="global.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hash.cpp" />
|
||||
<ClCompile Include="headers.cpp" />
|
||||
<ClCompile Include="isnt.cpp" />
|
||||
<ClCompile Include="match.cpp" />
|
||||
<ClCompile Include="options.cpp" />
|
||||
<ClCompile Include="pathfn.cpp" />
|
||||
<ClCompile Include="qopen.cpp" />
|
||||
<ClCompile Include="rar.cpp" />
|
||||
<ClCompile Include="rarpch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rarvm.cpp" />
|
||||
<ClCompile Include="rawread.cpp" />
|
||||
<ClCompile Include="rdwrfn.cpp" />
|
||||
<ClCompile Include="rijndael.cpp" />
|
||||
<ClCompile Include="rs.cpp" />
|
||||
<ClCompile Include="rs16.cpp" />
|
||||
<ClCompile Include="scantree.cpp" />
|
||||
<ClCompile Include="secpassword.cpp" />
|
||||
<ClCompile Include="sha1.cpp" />
|
||||
<ClCompile Include="sha256.cpp" />
|
||||
<ClCompile Include="smallfn.cpp" />
|
||||
<ClCompile Include="strfn.cpp" />
|
||||
<ClCompile Include="strlist.cpp" />
|
||||
<ClCompile Include="system.cpp" />
|
||||
<ClCompile Include="threadpool.cpp" />
|
||||
<ClCompile Include="timefn.cpp" />
|
||||
<ClCompile Include="ui.cpp" />
|
||||
<ClCompile Include="unicode.cpp" />
|
||||
<ClCompile Include="unpack.cpp" />
|
||||
<ClCompile Include="volume.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="rar.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="dll.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,92 +0,0 @@
|
||||
ACKNOWLEDGMENTS
|
||||
|
||||
* We used "Screaming Fast Galois Field Arithmetic Using Intel
|
||||
SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
|
||||
and Ethan L. Miller to improve Reed-Solomon coding performance.
|
||||
Also we are grateful to Artem Drobanov and Bulat Ziganshin
|
||||
for samples and ideas allowed to make Reed-Solomon coding
|
||||
more efficient.
|
||||
|
||||
* RAR text compression algorithm is based on Dmitry Shkarin PPMII
|
||||
and Dmitry Subbotin carryless rangecoder public domain source code.
|
||||
You may find it in ftp.elf.stuba.sk/pub/pc/pack.
|
||||
|
||||
* RAR encryption includes parts of code from Szymon Stefanek
|
||||
and Brian Gladman AES implementations also as Steve Reid SHA-1 source.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK.
|
||||
All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The free distribution and use of this software in both source and binary
|
||||
form is allowed (with or without changes) provided that:
|
||||
|
||||
1. distributions of this source code include the above copyright
|
||||
notice, this list of conditions and the following disclaimer;
|
||||
|
||||
2. distributions in binary form include the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other associated materials;
|
||||
|
||||
3. the copyright holder's name is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
ALTERNATIVELY, provided that this notice is retained in full, this product
|
||||
may be distributed under the terms of the GNU General Public License (GPL),
|
||||
in which case the provisions of the GPL apply INSTEAD OF those given above.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Source code of this package also as other cryptographic technology
|
||||
and computing project related links are available on Brian Gladman's
|
||||
web site: http://www.gladman.me.uk
|
||||
|
||||
* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm.
|
||||
Original Intel Slicing-by-8 code is available here:
|
||||
|
||||
http://sourceforge.net/projects/slicing-by-8/
|
||||
|
||||
Original Intel Slicing-by-8 code is licensed under BSD License
|
||||
available at http://www.opensource.org/licenses/bsd-license.html
|
||||
|
||||
Copyright (c) 2004-2006 Intel Corporation.
|
||||
All Rights Reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with
|
||||
the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
|
||||
designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
|
||||
and Christian Winnerlein.
|
||||
|
||||
* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
|
||||
to significantly improve RAR compression and speed.
|
||||
166
unrar/arccmt.cpp
166
unrar/arccmt.cpp
@@ -1,166 +0,0 @@
|
||||
static bool IsAnsiEscComment(const wchar *Data,size_t Size);
|
||||
|
||||
bool Archive::GetComment(Array<wchar> *CmtData)
|
||||
{
|
||||
if (!MainComment)
|
||||
return false;
|
||||
SaveFilePos SavePos(*this);
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
uint CmtLength;
|
||||
if (Format==RARFMT14)
|
||||
{
|
||||
Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET);
|
||||
CmtLength=GetByte();
|
||||
CmtLength+=(GetByte()<<8);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (MainHead.CommentInHeader)
|
||||
{
|
||||
// Old style (RAR 2.9) archive comment embedded into the main
|
||||
// archive header.
|
||||
Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET);
|
||||
ReadHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current (RAR 3.0+) version of archive comment.
|
||||
Seek(GetStartPos(),SEEK_SET);
|
||||
return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
|
||||
}
|
||||
#ifndef SFX_MODULE
|
||||
// Old style (RAR 2.9) comment header embedded into the main
|
||||
// archive header.
|
||||
if (BrokenHeader)
|
||||
{
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD;
|
||||
#endif
|
||||
}
|
||||
#ifndef SFX_MODULE
|
||||
if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30)
|
||||
{
|
||||
if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35))
|
||||
return false;
|
||||
ComprDataIO DataIO;
|
||||
DataIO.SetTestMode(true);
|
||||
uint UnpCmtLength;
|
||||
if (Format==RARFMT14)
|
||||
{
|
||||
#ifdef RAR_NOCRYPT
|
||||
return false;
|
||||
#else
|
||||
UnpCmtLength=GetByte();
|
||||
UnpCmtLength+=(GetByte()<<8);
|
||||
CmtLength-=2;
|
||||
DataIO.SetCmt13Encryption();
|
||||
CommHead.UnpVer=15;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
UnpCmtLength=CommHead.UnpSize;
|
||||
DataIO.SetFiles(this,NULL);
|
||||
DataIO.EnableShowProgress(false);
|
||||
DataIO.SetPackedSizeToRead(CmtLength);
|
||||
DataIO.UnpHash.Init(HASH_CRC32,1);
|
||||
|
||||
Unpack CmtUnpack(&DataIO);
|
||||
CmtUnpack.Init(0x10000,false);
|
||||
CmtUnpack.SetDestSize(UnpCmtLength);
|
||||
CmtUnpack.DoUnpack(CommHead.UnpVer,false);
|
||||
|
||||
if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC)
|
||||
{
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte *UnpData;
|
||||
size_t UnpDataSize;
|
||||
DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
|
||||
#ifdef _WIN_ALL
|
||||
// If we ever decide to extend it to Android, we'll need to alloc
|
||||
// 4x memory for OEM to UTF-8 output here.
|
||||
OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
|
||||
#endif
|
||||
CmtData->Alloc(UnpDataSize+1);
|
||||
memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
|
||||
CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
|
||||
CmtData->Alloc(wcslen(CmtData->Addr(0)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CmtLength==0)
|
||||
return false;
|
||||
Array<byte> CmtRaw(CmtLength);
|
||||
Read(&CmtRaw[0],CmtLength);
|
||||
|
||||
if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
|
||||
{
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
CmtData->Alloc(CmtLength+1);
|
||||
CmtRaw.Push(0);
|
||||
#ifdef _WIN_ALL
|
||||
// If we ever decide to extend it to Android, we'll need to alloc
|
||||
// 4x memory for OEM to UTF-8 output here.
|
||||
OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]);
|
||||
#endif
|
||||
CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtLength);
|
||||
CmtData->Alloc(wcslen(CmtData->Addr(0)));
|
||||
}
|
||||
#endif
|
||||
return CmtData->Size() > 0;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::ReadCommentData(Array<wchar> *CmtData)
|
||||
{
|
||||
Array<byte> CmtRaw;
|
||||
if (!ReadSubData(&CmtRaw,NULL))
|
||||
return false;
|
||||
size_t CmtSize=CmtRaw.Size();
|
||||
CmtRaw.Push(0);
|
||||
CmtData->Alloc(CmtSize+1);
|
||||
if (Format==RARFMT50)
|
||||
UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
|
||||
else
|
||||
if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
|
||||
{
|
||||
RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2);
|
||||
(*CmtData)[CmtSize/2]=0;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
|
||||
}
|
||||
CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Archive::ViewComment()
|
||||
{
|
||||
if (Cmd->DisableComment)
|
||||
return;
|
||||
Array<wchar> CmtBuf;
|
||||
if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments.
|
||||
{
|
||||
size_t CmtSize=CmtBuf.Size();
|
||||
wchar *ChPtr=wcschr(&CmtBuf[0],0x1A);
|
||||
if (ChPtr!=NULL)
|
||||
CmtSize=ChPtr-&CmtBuf[0];
|
||||
mprintf(L"\n");
|
||||
OutComment(&CmtBuf[0],CmtSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "arccmt.cpp"
|
||||
|
||||
|
||||
Archive::Archive(RAROptions *InitCmd)
|
||||
{
|
||||
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
|
||||
|
||||
DummyCmd=(InitCmd==NULL);
|
||||
Cmd=DummyCmd ? (new RAROptions):InitCmd;
|
||||
|
||||
OpenShared=Cmd->OpenShared;
|
||||
Format=RARFMT15;
|
||||
Solid=false;
|
||||
Volume=false;
|
||||
MainComment=false;
|
||||
Locked=false;
|
||||
Signed=false;
|
||||
FirstVolume=false;
|
||||
NewNumbering=false;
|
||||
SFXSize=0;
|
||||
LatestTime.Reset();
|
||||
Protected=false;
|
||||
Encrypted=false;
|
||||
FailedHeaderDecryption=false;
|
||||
BrokenHeader=false;
|
||||
LastReadBlock=0;
|
||||
|
||||
CurBlockPos=0;
|
||||
NextBlockPos=0;
|
||||
|
||||
RecoverySize=-1;
|
||||
RecoveryPercent=-1;
|
||||
|
||||
memset(&MainHead,0,sizeof(MainHead));
|
||||
memset(&CryptHead,0,sizeof(CryptHead));
|
||||
memset(&EndArcHead,0,sizeof(EndArcHead));
|
||||
|
||||
VolNumber=0;
|
||||
VolWrite=0;
|
||||
AddingFilesSize=0;
|
||||
AddingHeadersSize=0;
|
||||
*FirstVolumeName=0;
|
||||
|
||||
Splitting=false;
|
||||
NewArchive=false;
|
||||
|
||||
SilentOpen=false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Archive::~Archive()
|
||||
{
|
||||
if (DummyCmd)
|
||||
delete Cmd;
|
||||
}
|
||||
|
||||
|
||||
void Archive::CheckArc(bool EnableBroken)
|
||||
{
|
||||
if (!IsArchive(EnableBroken))
|
||||
{
|
||||
// If FailedHeaderDecryption is set, we already reported that archive
|
||||
// password is incorrect.
|
||||
if (!FailedHeaderDecryption)
|
||||
uiMsg(UIERROR_BADARCHIVE,FileName);
|
||||
ErrHandler.Exit(RARX_FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void Archive::CheckOpen(const wchar *Name)
|
||||
{
|
||||
TOpen(Name);
|
||||
CheckArc(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool Archive::WCheckOpen(const wchar *Name)
|
||||
{
|
||||
if (!WOpen(Name))
|
||||
return false;
|
||||
if (!IsArchive(false))
|
||||
{
|
||||
uiMsg(UIERROR_BADARCHIVE,FileName);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
|
||||
{
|
||||
RARFORMAT Type=RARFMT_NONE;
|
||||
if (Size>=1 && D[0]==0x52)
|
||||
#ifndef SFX_MODULE
|
||||
if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
|
||||
Type=RARFMT14;
|
||||
else
|
||||
#endif
|
||||
if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
|
||||
{
|
||||
// We check the last signature byte, so we can return a sensible
|
||||
// warning in case we'll want to change the archive format
|
||||
// sometimes in the future.
|
||||
if (D[6]==0)
|
||||
Type=RARFMT15;
|
||||
else
|
||||
if (D[6]==1)
|
||||
Type=RARFMT50;
|
||||
else
|
||||
if (D[6]==2)
|
||||
Type=RARFMT_FUTURE;
|
||||
}
|
||||
return Type;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::IsArchive(bool EnableBroken)
|
||||
{
|
||||
Encrypted=false;
|
||||
BrokenHeader=false; // Might be left from previous volume.
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
if (IsDevice())
|
||||
{
|
||||
uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
|
||||
return false;
|
||||
SFXSize=0;
|
||||
|
||||
RARFORMAT Type;
|
||||
if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14)
|
||||
Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<char> Buffer(MAXSFXSIZE);
|
||||
long CurPos=(long)Tell();
|
||||
int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
|
||||
for (int I=0;I<ReadSize;I++)
|
||||
if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
|
||||
{
|
||||
char *D=&Buffer[28-CurPos];
|
||||
if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
|
||||
continue;
|
||||
}
|
||||
SFXSize=CurPos+I;
|
||||
Seek(SFXSize,SEEK_SET);
|
||||
if (Format==RARFMT15 || Format==RARFMT50)
|
||||
Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
|
||||
break;
|
||||
}
|
||||
if (SFXSize==0)
|
||||
return false;
|
||||
}
|
||||
if (Format==RARFMT_FUTURE)
|
||||
{
|
||||
uiMsg(UIERROR_NEWRARFORMAT,FileName);
|
||||
return false;
|
||||
}
|
||||
if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
|
||||
{
|
||||
Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1);
|
||||
if (MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
|
||||
return false;
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD5;
|
||||
}
|
||||
else
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD3;
|
||||
|
||||
#ifdef RARDLL
|
||||
// If callback function is not set, we cannot get the password,
|
||||
// so we skip the initial header processing for encrypted header archive.
|
||||
// It leads to skipped archive comment, but the rest of archive data
|
||||
// is processed correctly.
|
||||
if (Cmd->Callback==NULL)
|
||||
SilentOpen=true;
|
||||
#endif
|
||||
|
||||
// Skip the archive encryption header if any and read the main header.
|
||||
while (ReadHeader()!=0)
|
||||
{
|
||||
HEADER_TYPE Type=GetHeaderType();
|
||||
// In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
|
||||
// avoid the password prompt.
|
||||
if (Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT)
|
||||
break;
|
||||
SeekToNext();
|
||||
}
|
||||
|
||||
// This check allows to make RS based recovery even if password is incorrect.
|
||||
// But we should not do it for EnableBroken or we'll get 'not RAR archive'
|
||||
// messages when extracting encrypted archives with wrong password.
|
||||
if (FailedHeaderDecryption && !EnableBroken)
|
||||
return false;
|
||||
|
||||
SeekToNext();
|
||||
if (BrokenHeader) // Main archive header is corrupt.
|
||||
{
|
||||
uiMsg(UIERROR_MHEADERBROKEN,FileName);
|
||||
if (!EnableBroken)
|
||||
return false;
|
||||
}
|
||||
|
||||
MainComment=MainHead.CommentInHeader;
|
||||
|
||||
// If we process non-encrypted archive or can request a password,
|
||||
// we set 'first volume' flag based on file attributes below.
|
||||
// It is necessary for RAR 2.x archives, which did not have 'first volume'
|
||||
// flag in main header. Also for all RAR formats we need to scan until
|
||||
// first file header to set "comment" flag when reading service header.
|
||||
// Unless we are in silent mode, we need to know about presence of comment
|
||||
// immediately after IsArchive call.
|
||||
if (!SilentOpen || !Encrypted)
|
||||
{
|
||||
SaveFilePos SavePos(*this);
|
||||
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
|
||||
HEADER_TYPE SaveCurHeaderType=CurHeaderType;
|
||||
|
||||
while (ReadHeader()!=0)
|
||||
{
|
||||
HEADER_TYPE HeaderType=GetHeaderType();
|
||||
if (HeaderType==HEAD_SERVICE)
|
||||
{
|
||||
// If we have a split service headers, it surely indicates non-first
|
||||
// volume. But not split service header does not guarantee the first
|
||||
// volume, because we can have split file after non-split archive
|
||||
// comment. So we do not quit from loop here.
|
||||
FirstVolume=Volume && !SubHead.SplitBefore;
|
||||
}
|
||||
else
|
||||
if (HeaderType==HEAD_FILE)
|
||||
{
|
||||
FirstVolume=Volume && !FileHead.SplitBefore;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
|
||||
break;
|
||||
SeekToNext();
|
||||
}
|
||||
CurBlockPos=SaveCurBlockPos;
|
||||
NextBlockPos=SaveNextBlockPos;
|
||||
CurHeaderType=SaveCurHeaderType;
|
||||
}
|
||||
if (!Volume || FirstVolume)
|
||||
wcscpy(FirstVolumeName,FileName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Archive::SeekToNext()
|
||||
{
|
||||
Seek(NextBlockPos,SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Calculate the block size including encryption fields and padding if any.
|
||||
uint Archive::FullHeaderSize(size_t Size)
|
||||
{
|
||||
if (Encrypted)
|
||||
{
|
||||
Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
|
||||
if (Format == RARFMT50)
|
||||
Size += SIZE_INITV;
|
||||
else
|
||||
Size += SIZE_SALT30;
|
||||
}
|
||||
return uint(Size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef USE_QOPEN
|
||||
bool Archive::Open(const wchar *Name,uint Mode)
|
||||
{
|
||||
// Important if we reuse Archive object and it has virtual QOpen
|
||||
// file position not matching real. For example, for 'l -v volname'.
|
||||
QOpen.Unload();
|
||||
|
||||
return File::Open(Name,Mode);
|
||||
}
|
||||
|
||||
|
||||
int Archive::Read(void *Data,size_t Size)
|
||||
{
|
||||
size_t Result;
|
||||
if (QOpen.Read(Data,Size,Result))
|
||||
return (int)Result;
|
||||
return File::Read(Data,Size);
|
||||
}
|
||||
|
||||
|
||||
void Archive::Seek(int64 Offset,int Method)
|
||||
{
|
||||
if (!QOpen.Seek(Offset,Method))
|
||||
File::Seek(Offset,Method);
|
||||
}
|
||||
|
||||
|
||||
int64 Archive::Tell()
|
||||
{
|
||||
int64 QPos;
|
||||
if (QOpen.Tell(&QPos))
|
||||
return QPos;
|
||||
return File::Tell();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
#ifndef _RAR_ARCHIVE_
|
||||
#define _RAR_ARCHIVE_
|
||||
|
||||
class PPack;
|
||||
class RawRead;
|
||||
class RawWrite;
|
||||
|
||||
enum NOMODIFY_FLAGS
|
||||
{
|
||||
NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4
|
||||
};
|
||||
|
||||
enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
|
||||
|
||||
enum ADDSUBDATA_FLAGS
|
||||
{
|
||||
ASDF_SPLIT = 1, // Allow to split archive just before header if necessary.
|
||||
ASDF_COMPRESS = 2, // Allow to compress data following subheader.
|
||||
ASDF_CRYPT = 4, // Encrypt data after subheader if password is set.
|
||||
ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode.
|
||||
};
|
||||
|
||||
class Archive:public File
|
||||
{
|
||||
private:
|
||||
void UpdateLatestTime(FileHeader *CurBlock);
|
||||
void ConvertNameCase(wchar *Name);
|
||||
void ConvertFileHeader(FileHeader *hd);
|
||||
void WriteBlock50(HEADER_TYPE HeaderType,BaseBlock *wb,bool OnlySetSize,bool NonFinalWrite);
|
||||
size_t ReadHeader14();
|
||||
size_t ReadHeader15();
|
||||
size_t ReadHeader50();
|
||||
void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
|
||||
void RequestArcPassword();
|
||||
void UnexpEndArcMsg();
|
||||
void BrokenHeaderMsg();
|
||||
void UnkEncVerMsg(const wchar *Name);
|
||||
void UnkEncVerMsg();
|
||||
bool ReadCommentData(Array<wchar> *CmtData);
|
||||
|
||||
#if !defined(RAR_NOCRYPT)
|
||||
CryptData HeadersCrypt;
|
||||
#endif
|
||||
ComprDataIO SubDataIO;
|
||||
bool DummyCmd;
|
||||
RAROptions *Cmd;
|
||||
|
||||
int64 RecoverySize;
|
||||
int RecoveryPercent;
|
||||
|
||||
RarTime LatestTime;
|
||||
int LastReadBlock;
|
||||
HEADER_TYPE CurHeaderType;
|
||||
|
||||
bool SilentOpen;
|
||||
#ifdef USE_QOPEN
|
||||
QuickOpen QOpen;
|
||||
#endif
|
||||
public:
|
||||
Archive(RAROptions *InitCmd=NULL);
|
||||
~Archive();
|
||||
static RARFORMAT IsSignature(const byte *D,size_t Size);
|
||||
bool IsArchive(bool EnableBroken);
|
||||
size_t SearchBlock(HEADER_TYPE HeaderType);
|
||||
size_t SearchSubBlock(const wchar *Type);
|
||||
size_t SearchRR();
|
||||
void WriteBlock(HEADER_TYPE HeaderType,BaseBlock *wb=NULL,bool OnlySetSize=false,bool NonFinalWrite=false);
|
||||
void SetBlockSize(HEADER_TYPE HeaderType,BaseBlock *wb=NULL) {WriteBlock(HeaderType,wb,true);}
|
||||
size_t ReadHeader();
|
||||
void CheckArc(bool EnableBroken);
|
||||
void CheckOpen(const wchar *Name);
|
||||
bool WCheckOpen(const wchar *Name);
|
||||
bool GetComment(Array<wchar> *CmtData);
|
||||
void ViewComment();
|
||||
void SetLatestTime(RarTime *NewTime);
|
||||
void SeekToNext();
|
||||
bool CheckAccess();
|
||||
bool IsArcDir();
|
||||
void ConvertAttributes();
|
||||
void VolSubtractHeaderSize(size_t SubSize);
|
||||
uint FullHeaderSize(size_t Size);
|
||||
int64 GetStartPos();
|
||||
void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
|
||||
const wchar *Name,uint Flags);
|
||||
bool ReadSubData(Array<byte> *UnpData,File *DestFile);
|
||||
HEADER_TYPE GetHeaderType() {return CurHeaderType;};
|
||||
RAROptions* GetRAROptions() {return Cmd;}
|
||||
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
|
||||
#if 0
|
||||
void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
|
||||
#endif
|
||||
#ifdef USE_QOPEN
|
||||
bool Open(const wchar *Name,uint Mode=FMF_READ);
|
||||
int Read(void *Data,size_t Size);
|
||||
void Seek(int64 Offset,int Method);
|
||||
int64 Tell();
|
||||
void QOpenUnload() {QOpen.Unload();}
|
||||
#endif
|
||||
|
||||
BaseBlock ShortBlock;
|
||||
MarkHeader MarkHead;
|
||||
MainHeader MainHead;
|
||||
CryptHeader CryptHead;
|
||||
FileHeader FileHead;
|
||||
EndArcHeader EndArcHead;
|
||||
SubBlockHeader SubBlockHead;
|
||||
FileHeader SubHead;
|
||||
CommentHeader CommHead;
|
||||
ProtectHeader ProtectHead;
|
||||
AVHeader AVHead;
|
||||
SignHeader SignHead;
|
||||
UnixOwnersHeader UOHead;
|
||||
MacFInfoHeader MACHead;
|
||||
EAHeader EAHead;
|
||||
StreamHeader StreamHead;
|
||||
|
||||
int64 CurBlockPos;
|
||||
int64 NextBlockPos;
|
||||
|
||||
RARFORMAT Format;
|
||||
bool Solid;
|
||||
bool Volume;
|
||||
bool MainComment;
|
||||
bool Locked;
|
||||
bool Signed;
|
||||
bool FirstVolume;
|
||||
bool NewNumbering;
|
||||
bool Protected;
|
||||
bool Encrypted;
|
||||
size_t SFXSize;
|
||||
bool BrokenHeader;
|
||||
bool FailedHeaderDecryption;
|
||||
|
||||
#if !defined(RAR_NOCRYPT)
|
||||
byte ArcSalt[SIZE_SALT50];
|
||||
#endif
|
||||
|
||||
bool Splitting;
|
||||
|
||||
uint VolNumber;
|
||||
int64 VolWrite;
|
||||
uint64 AddingFilesSize;
|
||||
uint64 AddingHeadersSize;
|
||||
|
||||
bool NewArchive;
|
||||
|
||||
wchar FirstVolumeName[NM];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
1437
unrar/arcread.cpp
1437
unrar/arcread.cpp
File diff suppressed because it is too large
Load Diff
191
unrar/array.hpp
191
unrar/array.hpp
@@ -1,191 +0,0 @@
|
||||
#ifndef _RAR_ARRAY_
|
||||
#define _RAR_ARRAY_
|
||||
|
||||
extern ErrorHandler ErrHandler;
|
||||
|
||||
template <class T> class Array
|
||||
{
|
||||
private:
|
||||
T *Buffer;
|
||||
size_t BufSize;
|
||||
size_t AllocSize;
|
||||
size_t MaxSize;
|
||||
bool Secure; // Clean memory if true.
|
||||
public:
|
||||
Array();
|
||||
Array(size_t Size);
|
||||
Array(const Array &Src); // Copy constructor.
|
||||
~Array();
|
||||
inline void CleanData();
|
||||
inline T& operator [](size_t Item) const;
|
||||
inline T* operator + (size_t Pos);
|
||||
inline size_t Size(); // Returns the size in items, not in bytes.
|
||||
void Add(size_t Items);
|
||||
void Alloc(size_t Items);
|
||||
void Reset();
|
||||
void SoftReset();
|
||||
void operator = (Array<T> &Src);
|
||||
void Push(T Item);
|
||||
void Append(T *Item,size_t Count);
|
||||
T* Addr(size_t Item) {return Buffer+Item;}
|
||||
void SetMaxSize(size_t Size) {MaxSize=Size;}
|
||||
T* Begin() {return Buffer;}
|
||||
T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;}
|
||||
void SetSecure() {Secure=true;}
|
||||
};
|
||||
|
||||
|
||||
template <class T> void Array<T>::CleanData()
|
||||
{
|
||||
Buffer=NULL;
|
||||
BufSize=0;
|
||||
AllocSize=0;
|
||||
MaxSize=0;
|
||||
Secure=false;
|
||||
}
|
||||
|
||||
|
||||
template <class T> Array<T>::Array()
|
||||
{
|
||||
CleanData();
|
||||
}
|
||||
|
||||
|
||||
template <class T> Array<T>::Array(size_t Size)
|
||||
{
|
||||
CleanData();
|
||||
Add(Size);
|
||||
}
|
||||
|
||||
|
||||
// Copy constructor in case we need to pass an object as value.
|
||||
template <class T> Array<T>::Array(const Array &Src)
|
||||
{
|
||||
CleanData();
|
||||
Alloc(Src.BufSize);
|
||||
if (Src.BufSize!=0)
|
||||
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
template <class T> Array<T>::~Array()
|
||||
{
|
||||
if (Buffer!=NULL)
|
||||
{
|
||||
if (Secure)
|
||||
cleandata(Buffer,AllocSize*sizeof(T));
|
||||
free(Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class T> inline T& Array<T>::operator [](size_t Item) const
|
||||
{
|
||||
return Buffer[Item];
|
||||
}
|
||||
|
||||
|
||||
template <class T> inline T* Array<T>::operator +(size_t Pos)
|
||||
{
|
||||
return Buffer+Pos;
|
||||
}
|
||||
|
||||
|
||||
template <class T> inline size_t Array<T>::Size()
|
||||
{
|
||||
return BufSize;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Add(size_t Items)
|
||||
{
|
||||
BufSize+=Items;
|
||||
if (BufSize>AllocSize)
|
||||
{
|
||||
if (MaxSize!=0 && BufSize>MaxSize)
|
||||
{
|
||||
ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize);
|
||||
ErrHandler.MemoryError();
|
||||
}
|
||||
|
||||
size_t Suggested=AllocSize+AllocSize/4+32;
|
||||
size_t NewSize=Max(BufSize,Suggested);
|
||||
|
||||
T *NewBuffer;
|
||||
if (Secure)
|
||||
{
|
||||
NewBuffer=(T *)malloc(NewSize*sizeof(T));
|
||||
if (NewBuffer==NULL)
|
||||
ErrHandler.MemoryError();
|
||||
if (Buffer!=NULL)
|
||||
{
|
||||
memcpy(NewBuffer,Buffer,AllocSize*sizeof(T));
|
||||
cleandata(Buffer,AllocSize*sizeof(T));
|
||||
free(Buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
|
||||
if (NewBuffer==NULL)
|
||||
ErrHandler.MemoryError();
|
||||
}
|
||||
Buffer=NewBuffer;
|
||||
AllocSize=NewSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Alloc(size_t Items)
|
||||
{
|
||||
if (Items>AllocSize)
|
||||
Add(Items-BufSize);
|
||||
else
|
||||
BufSize=Items;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Reset()
|
||||
{
|
||||
if (Buffer!=NULL)
|
||||
{
|
||||
free(Buffer);
|
||||
Buffer=NULL;
|
||||
}
|
||||
BufSize=0;
|
||||
AllocSize=0;
|
||||
}
|
||||
|
||||
|
||||
// Reset buffer size, but preserve already allocated memory if any,
|
||||
// so we can reuse it without wasting time to allocation.
|
||||
template <class T> void Array<T>::SoftReset()
|
||||
{
|
||||
BufSize=0;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::operator =(Array<T> &Src)
|
||||
{
|
||||
Reset();
|
||||
Alloc(Src.BufSize);
|
||||
if (Src.BufSize!=0)
|
||||
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Push(T Item)
|
||||
{
|
||||
Add(1);
|
||||
(*this)[Size()-1]=Item;
|
||||
}
|
||||
|
||||
|
||||
template <class T> void Array<T>::Append(T *Items,size_t Count)
|
||||
{
|
||||
size_t CurSize=Size();
|
||||
Add(Count);
|
||||
memcpy(Buffer+CurSize,Items,Count*sizeof(T));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,183 +0,0 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
|
||||
#include "rar.hpp"
|
||||
|
||||
#ifdef USE_SSE
|
||||
#include "blake2s_sse.cpp"
|
||||
#endif
|
||||
|
||||
static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth);
|
||||
static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen );
|
||||
static void blake2s_final( blake2s_state *S, byte *digest );
|
||||
|
||||
#include "blake2sp.cpp"
|
||||
|
||||
static const uint32 blake2s_IV[8] =
|
||||
{
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
static const byte blake2s_sigma[10][16] =
|
||||
{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
|
||||
};
|
||||
|
||||
static inline void blake2s_set_lastnode( blake2s_state *S )
|
||||
{
|
||||
S->f[1] = ~0U;
|
||||
}
|
||||
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static inline void blake2s_set_lastblock( blake2s_state *S )
|
||||
{
|
||||
if( S->last_node ) blake2s_set_lastnode( S );
|
||||
|
||||
S->f[0] = ~0U;
|
||||
}
|
||||
|
||||
|
||||
static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc )
|
||||
{
|
||||
S->t[0] += inc;
|
||||
S->t[1] += ( S->t[0] < inc );
|
||||
}
|
||||
|
||||
|
||||
/* init2 xors IV with input parameter block */
|
||||
void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth)
|
||||
{
|
||||
#ifdef USE_SSE
|
||||
if (_SSE_Version>=SSE_SSE2)
|
||||
blake2s_init_sse();
|
||||
#endif
|
||||
|
||||
S->init(); // Clean data.
|
||||
for( int i = 0; i < 8; ++i )
|
||||
S->h[i] = blake2s_IV[i];
|
||||
|
||||
S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block.
|
||||
S->h[2] ^= node_offset;
|
||||
S->h[3] ^= (node_depth<<16)|0x20000000;
|
||||
}
|
||||
|
||||
|
||||
#define G(r,i,m,a,b,c,d) \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+0]]; \
|
||||
d = rotr32(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 12); \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+1]]; \
|
||||
d = rotr32(d ^ a, 8); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 7);
|
||||
|
||||
|
||||
static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
uint32 m[16];
|
||||
uint32 v[16];
|
||||
|
||||
for( size_t i = 0; i < 16; ++i )
|
||||
m[i] = RawGet4( block + i * 4 );
|
||||
|
||||
for( size_t i = 0; i < 8; ++i )
|
||||
v[i] = S->h[i];
|
||||
|
||||
v[ 8] = blake2s_IV[0];
|
||||
v[ 9] = blake2s_IV[1];
|
||||
v[10] = blake2s_IV[2];
|
||||
v[11] = blake2s_IV[3];
|
||||
v[12] = S->t[0] ^ blake2s_IV[4];
|
||||
v[13] = S->t[1] ^ blake2s_IV[5];
|
||||
v[14] = S->f[0] ^ blake2s_IV[6];
|
||||
v[15] = S->f[1] ^ blake2s_IV[7];
|
||||
|
||||
for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows.
|
||||
{
|
||||
G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]);
|
||||
G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]);
|
||||
G(r,2,m,v[ 2],v[ 6],v[10],v[14]);
|
||||
G(r,3,m,v[ 3],v[ 7],v[11],v[15]);
|
||||
G(r,4,m,v[ 0],v[ 5],v[10],v[15]);
|
||||
G(r,5,m,v[ 1],v[ 6],v[11],v[12]);
|
||||
G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]);
|
||||
G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]);
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 8; ++i )
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
|
||||
void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
|
||||
{
|
||||
while( inlen > 0 )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
|
||||
|
||||
if( inlen > fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill ); // Fill buffer
|
||||
S->buflen += fill;
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
#ifdef USE_SSE
|
||||
#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode.
|
||||
if (_SSE_Version>=SSE_SSE2)
|
||||
#else
|
||||
if (_SSE_Version>=SSE_SSSE3)
|
||||
#endif
|
||||
blake2s_compress_sse( S, S->buf );
|
||||
else
|
||||
blake2s_compress( S, S->buf ); // Compress
|
||||
#else
|
||||
blake2s_compress( S, S->buf ); // Compress
|
||||
#endif
|
||||
|
||||
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
|
||||
S->buflen -= BLAKE2S_BLOCKBYTES;
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
}
|
||||
else // inlen <= fill
|
||||
{
|
||||
memcpy( S->buf + left, in, (size_t)inlen );
|
||||
S->buflen += (size_t)inlen; // Be lazy, do not compress
|
||||
in += inlen;
|
||||
inlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blake2s_final( blake2s_state *S, byte *digest )
|
||||
{
|
||||
if( S->buflen > BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
blake2s_compress( S, S->buf );
|
||||
S->buflen -= BLAKE2S_BLOCKBYTES;
|
||||
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
|
||||
}
|
||||
|
||||
blake2s_increment_counter( S, ( uint32 )S->buflen );
|
||||
blake2s_set_lastblock( S );
|
||||
memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
|
||||
blake2s_compress( S, S->buf );
|
||||
|
||||
for( int i = 0; i < 8; ++i ) /* Output full hash */
|
||||
RawPut4( S->h[i], digest + 4 * i );
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
#ifndef _RAR_BLAKE2_
|
||||
#define _RAR_BLAKE2_
|
||||
|
||||
#define BLAKE2_DIGEST_SIZE 32
|
||||
|
||||
enum blake2s_constant
|
||||
{
|
||||
BLAKE2S_BLOCKBYTES = 64,
|
||||
BLAKE2S_OUTBYTES = 32
|
||||
};
|
||||
|
||||
|
||||
// Alignment to 64 improves performance of both SSE and non-SSE versions.
|
||||
// Alignment to n*16 is required for SSE version, so we selected 64.
|
||||
// We use the custom alignment scheme instead of __declspec(align(x)),
|
||||
// because it is less compiler dependent. Also the compiler directive
|
||||
// does not help if structure is a member of class allocated through
|
||||
// 'new' operator.
|
||||
struct blake2s_state
|
||||
{
|
||||
enum { BLAKE_ALIGNMENT = 64 };
|
||||
|
||||
// buffer and uint32 h[8], t[2], f[2];
|
||||
enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
|
||||
|
||||
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
|
||||
|
||||
byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES].
|
||||
uint32 *h, *t, *f; // uint32 h[8], t[2], f[2].
|
||||
|
||||
size_t buflen;
|
||||
byte last_node;
|
||||
|
||||
blake2s_state()
|
||||
{
|
||||
set_pointers();
|
||||
}
|
||||
|
||||
// Required when we declare and assign in the same command.
|
||||
blake2s_state(blake2s_state &st)
|
||||
{
|
||||
set_pointers();
|
||||
*this=st;
|
||||
}
|
||||
|
||||
void set_pointers()
|
||||
{
|
||||
// Set aligned pointers. Must be done in constructor, not in Init(),
|
||||
// so assignments like 'blake2sp_state res=blake2ctx' work correctly
|
||||
// even if blake2sp_init is not called for 'res'.
|
||||
buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT);
|
||||
h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES);
|
||||
t = h + 8;
|
||||
f = t + 2;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
memset( ubuf, 0, sizeof( ubuf ) );
|
||||
buflen = 0;
|
||||
last_node = 0;
|
||||
}
|
||||
|
||||
// Since we use pointers, the default = would work incorrectly.
|
||||
blake2s_state& operator = (blake2s_state &st)
|
||||
{
|
||||
if (this != &st)
|
||||
{
|
||||
memcpy(buf, st.buf, BLAKE_DATA_SIZE);
|
||||
buflen = st.buflen;
|
||||
last_node = st.last_node;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef RAR_SMP
|
||||
class ThreadPool;
|
||||
#endif
|
||||
|
||||
struct blake2sp_state
|
||||
{
|
||||
blake2s_state S[8];
|
||||
blake2s_state R;
|
||||
byte buf[8 * BLAKE2S_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
|
||||
#ifdef RAR_SMP
|
||||
ThreadPool *ThPool;
|
||||
uint MaxThreads;
|
||||
#endif
|
||||
};
|
||||
|
||||
void blake2sp_init( blake2sp_state *S );
|
||||
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen );
|
||||
void blake2sp_final( blake2sp_state *S, byte *digest );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
|
||||
extern const byte blake2s_sigma[10][16];
|
||||
|
||||
// Initialization vector.
|
||||
static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
|
||||
|
||||
#ifdef _WIN_64
|
||||
// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro.
|
||||
static __m128i crotr8, crotr16;
|
||||
#endif
|
||||
|
||||
static void blake2s_init_sse()
|
||||
{
|
||||
// We cannot initialize these 128 bit variables in place when declaring
|
||||
// them globally, because global scope initialization is performed before
|
||||
// our SSE check and it would make code incompatible with older non-SSE2
|
||||
// CPUs. Also we cannot initialize them as static inside of function
|
||||
// using these variables, because SSE static initialization is not thread
|
||||
// safe: first thread starts initialization and sets "init done" flag even
|
||||
// if it is not done yet, second thread can attempt to access half-init
|
||||
// SSE data. So we moved init code here.
|
||||
|
||||
blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
|
||||
blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
|
||||
|
||||
#ifdef _WIN_64
|
||||
crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
|
||||
crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
|
||||
#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
|
||||
|
||||
#ifdef _WIN_32
|
||||
// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient
|
||||
// to not use _mm_shuffle_epi8 here.
|
||||
#define mm_rotr_epi32(r, c) ( \
|
||||
_mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
|
||||
#else
|
||||
#define mm_rotr_epi32(r, c) ( \
|
||||
c==8 ? _mm_shuffle_epi8(r,crotr8) \
|
||||
: c==16 ? _mm_shuffle_epi8(r,crotr16) \
|
||||
: _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
|
||||
#endif
|
||||
|
||||
|
||||
#define G1(row1,row2,row3,row4,buf) \
|
||||
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
|
||||
row4 = _mm_xor_si128( row4, row1 ); \
|
||||
row4 = mm_rotr_epi32(row4, 16); \
|
||||
row3 = _mm_add_epi32( row3, row4 ); \
|
||||
row2 = _mm_xor_si128( row2, row3 ); \
|
||||
row2 = mm_rotr_epi32(row2, 12);
|
||||
|
||||
#define G2(row1,row2,row3,row4,buf) \
|
||||
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
|
||||
row4 = _mm_xor_si128( row4, row1 ); \
|
||||
row4 = mm_rotr_epi32(row4, 8); \
|
||||
row3 = _mm_add_epi32( row3, row4 ); \
|
||||
row2 = _mm_xor_si128( row2, row3 ); \
|
||||
row2 = mm_rotr_epi32(row2, 7);
|
||||
|
||||
#define DIAGONALIZE(row1,row2,row3,row4) \
|
||||
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
|
||||
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
|
||||
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
|
||||
|
||||
#define UNDIAGONALIZE(row1,row2,row3,row4) \
|
||||
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
|
||||
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
|
||||
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
|
||||
|
||||
#ifdef _WIN_64
|
||||
// MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load
|
||||
// from stack operations, which are slower than this code.
|
||||
#define _mm_set_epi32(i3,i2,i1,i0) \
|
||||
_mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \
|
||||
_mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3)))
|
||||
#endif
|
||||
|
||||
// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
|
||||
// and about the same in x64 mode in our test. Perhaps depends on compiler.
|
||||
// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather
|
||||
// instructions here, but they did not show any speed gain on i7-6700K.
|
||||
#define SSE_ROUND(m,row,r) \
|
||||
{ \
|
||||
__m128i buf; \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \
|
||||
G1(row[0],row[1],row[2],row[3],buf); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \
|
||||
G2(row[0],row[1],row[2],row[3],buf); \
|
||||
DIAGONALIZE(row[0],row[1],row[2],row[3]); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \
|
||||
G1(row[0],row[1],row[2],row[3],buf); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \
|
||||
G2(row[0],row[1],row[2],row[3],buf); \
|
||||
UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \
|
||||
}
|
||||
|
||||
|
||||
static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
__m128i row[4];
|
||||
__m128i ff0, ff1;
|
||||
|
||||
const uint32 *m = ( uint32 * )block;
|
||||
|
||||
row[0] = ff0 = LOAD( &S->h[0] );
|
||||
row[1] = ff1 = LOAD( &S->h[4] );
|
||||
|
||||
row[2] = blake2s_IV_0_3;
|
||||
row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) );
|
||||
SSE_ROUND( m, row, 0 );
|
||||
SSE_ROUND( m, row, 1 );
|
||||
SSE_ROUND( m, row, 2 );
|
||||
SSE_ROUND( m, row, 3 );
|
||||
SSE_ROUND( m, row, 4 );
|
||||
SSE_ROUND( m, row, 5 );
|
||||
SSE_ROUND( m, row, 6 );
|
||||
SSE_ROUND( m, row, 7 );
|
||||
SSE_ROUND( m, row, 8 );
|
||||
SSE_ROUND( m, row, 9 );
|
||||
STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) );
|
||||
STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) );
|
||||
return 0;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along with
|
||||
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#define PARALLELISM_DEGREE 8
|
||||
|
||||
void blake2sp_init( blake2sp_state *S )
|
||||
{
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
|
||||
blake2s_init_param( &S->R, 0, 1 ); // Init root.
|
||||
|
||||
for( uint i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
|
||||
|
||||
S->R.last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1].last_node = 1;
|
||||
}
|
||||
|
||||
|
||||
struct Blake2ThreadData
|
||||
{
|
||||
void Update();
|
||||
blake2s_state *S;
|
||||
const byte *in;
|
||||
size_t inlen;
|
||||
};
|
||||
|
||||
|
||||
void Blake2ThreadData::Update()
|
||||
{
|
||||
size_t inlen__ = inlen;
|
||||
const byte *in__ = ( const byte * )in;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
#ifdef USE_SSE
|
||||
// We gain 5% in i7 SSE mode by prefetching next data block.
|
||||
if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
|
||||
_mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
|
||||
#endif
|
||||
blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RAR_SMP
|
||||
THREAD_PROC(Blake2Thread)
|
||||
{
|
||||
Blake2ThreadData *td=(Blake2ThreadData *)Data;
|
||||
td->Update();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = sizeof( S->buf ) - left;
|
||||
|
||||
if( left && inlen >= fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill );
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
Blake2ThreadData btd_array[PARALLELISM_DEGREE];
|
||||
|
||||
#ifdef RAR_SMP
|
||||
uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads;
|
||||
|
||||
if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here.
|
||||
ThreadNumber=4;
|
||||
#else
|
||||
uint ThreadNumber=1;
|
||||
#endif
|
||||
|
||||
for (size_t id__=0;id__<PARALLELISM_DEGREE;)
|
||||
{
|
||||
for (uint Thread=0;Thread<ThreadNumber && id__<PARALLELISM_DEGREE;Thread++)
|
||||
{
|
||||
Blake2ThreadData *btd=btd_array+Thread;
|
||||
|
||||
btd->inlen = inlen;
|
||||
btd->in = in + id__ * BLAKE2S_BLOCKBYTES;
|
||||
btd->S = &S->S[id__];
|
||||
|
||||
#ifdef RAR_SMP
|
||||
if (ThreadNumber>1)
|
||||
S->ThPool->AddTask(Blake2Thread,(void*)btd);
|
||||
else
|
||||
btd->Update();
|
||||
#else
|
||||
btd->Update();
|
||||
#endif
|
||||
id__++;
|
||||
}
|
||||
#ifdef RAR_SMP
|
||||
if (S->ThPool!=NULL) // Can be NULL in -mt1 mode.
|
||||
S->ThPool->WaitDone();
|
||||
#endif // RAR_SMP
|
||||
}
|
||||
|
||||
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
|
||||
inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( inlen > 0 )
|
||||
memcpy( S->buf + left, in, (size_t)inlen );
|
||||
|
||||
S->buflen = left + (size_t)inlen;
|
||||
}
|
||||
|
||||
|
||||
void blake2sp_final( blake2sp_state *S, byte *digest )
|
||||
{
|
||||
byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
{
|
||||
if( S->buflen > i * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
|
||||
|
||||
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
|
||||
}
|
||||
|
||||
blake2s_final( &S->S[i], hash[i] );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES );
|
||||
|
||||
blake2s_final( &S->R, digest );
|
||||
}
|
||||
1400
unrar/cmddata.cpp
1400
unrar/cmddata.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,64 +0,0 @@
|
||||
#ifndef _RAR_CMDDATA_
|
||||
#define _RAR_CMDDATA_
|
||||
|
||||
|
||||
#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx"
|
||||
|
||||
enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
|
||||
|
||||
class CommandData:public RAROptions
|
||||
{
|
||||
private:
|
||||
void ProcessSwitchesString(const wchar *Str);
|
||||
void ProcessSwitch(const wchar *Switch);
|
||||
void BadSwitch(const wchar *Switch);
|
||||
uint GetExclAttr(const wchar *Str);
|
||||
|
||||
bool FileLists;
|
||||
bool NoMoreSwitches;
|
||||
RAR_CMD_LIST_MODE ListMode;
|
||||
bool BareOutput;
|
||||
public:
|
||||
CommandData();
|
||||
void Init();
|
||||
|
||||
void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
|
||||
void ParseArg(wchar *ArgW);
|
||||
void ParseDone();
|
||||
void ParseEnvVar();
|
||||
void ReadConfig();
|
||||
void PreprocessArg(const wchar *Arg);
|
||||
void OutTitle();
|
||||
void OutHelp(RAR_EXIT ExitCode);
|
||||
bool IsSwitch(int Ch);
|
||||
bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
|
||||
static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode);
|
||||
bool ExclDirByAttr(uint FileAttr);
|
||||
bool TimeCheck(RarTime &ft);
|
||||
bool SizeCheck(int64 Size);
|
||||
bool AnyFiltersActive();
|
||||
int IsProcessFile(FileHeader &FileHead,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH,
|
||||
wchar *MatchedArg=NULL,uint MatchedArgSize=0);
|
||||
void ProcessCommand();
|
||||
void AddArcName(const wchar *Name);
|
||||
bool GetArcName(wchar *Name,int MaxSize);
|
||||
bool CheckWinSize();
|
||||
|
||||
int GetRecoverySize(const wchar *Str,int DefSize);
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
void ReportWrongSwitches(RARFORMAT Format);
|
||||
#endif
|
||||
|
||||
wchar Command[NM+16];
|
||||
|
||||
wchar ArcName[NM];
|
||||
|
||||
StringList FileArgs;
|
||||
StringList ExclArgs;
|
||||
StringList InclArgs;
|
||||
StringList ArcNames;
|
||||
StringList StoreArgs;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
|
||||
inline unsigned int RangeCoder::GetChar()
|
||||
{
|
||||
return(UnpackRead->GetChar());
|
||||
}
|
||||
|
||||
|
||||
void RangeCoder::InitDecoder(Unpack *UnpackRead)
|
||||
{
|
||||
RangeCoder::UnpackRead=UnpackRead;
|
||||
|
||||
low=code=0;
|
||||
range=uint(-1);
|
||||
for (int i=0;i < 4;i++)
|
||||
code=(code << 8) | GetChar();
|
||||
}
|
||||
|
||||
|
||||
// (int) cast before "low" added only to suppress compiler warnings.
|
||||
#define ARI_DEC_NORMALIZE(code,low,range,read) \
|
||||
{ \
|
||||
while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \
|
||||
{ \
|
||||
code=(code << 8) | read->GetChar(); \
|
||||
range <<= 8; \
|
||||
low <<= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
inline int RangeCoder::GetCurrentCount()
|
||||
{
|
||||
return (code-low)/(range /= SubRange.scale);
|
||||
}
|
||||
|
||||
|
||||
inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT)
|
||||
{
|
||||
return (code-low)/(range >>= SHIFT);
|
||||
}
|
||||
|
||||
|
||||
inline void RangeCoder::Decode()
|
||||
{
|
||||
low += range*SubRange.LowCount;
|
||||
range *= SubRange.HighCount-SubRange.LowCount;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/****************************************************************************
|
||||
* Contents: 'Carryless rangecoder' by Dmitry Subbotin *
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class RangeCoder
|
||||
{
|
||||
public:
|
||||
void InitDecoder(Unpack *UnpackRead);
|
||||
inline int GetCurrentCount();
|
||||
inline uint GetCurrentShiftCount(uint SHIFT);
|
||||
inline void Decode();
|
||||
inline void PutChar(unsigned int c);
|
||||
inline unsigned int GetChar();
|
||||
|
||||
uint low, code, range;
|
||||
struct SUBRANGE
|
||||
{
|
||||
uint LowCount, HighCount, scale;
|
||||
} SubRange;
|
||||
|
||||
Unpack *UnpackRead;
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef _RAR_COMPRESS_
|
||||
#define _RAR_COMPRESS_
|
||||
|
||||
// Combine pack and unpack constants to class to avoid polluting global
|
||||
// namespace with numerous short names.
|
||||
class PackDef
|
||||
{
|
||||
public:
|
||||
static const uint MAX_LZ_MATCH = 0x1001;
|
||||
static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
|
||||
static const uint LOW_DIST_REP_COUNT = 16;
|
||||
|
||||
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC = 64;
|
||||
static const uint LDC = 16;
|
||||
static const uint RC = 44;
|
||||
static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC;
|
||||
static const uint BC = 20;
|
||||
|
||||
static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC30 = 60;
|
||||
static const uint LDC30 = 17;
|
||||
static const uint RC30 = 28;
|
||||
static const uint BC30 = 20;
|
||||
static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30;
|
||||
|
||||
static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC20 = 48;
|
||||
static const uint RC20 = 28;
|
||||
static const uint BC20 = 19;
|
||||
static const uint MC20 = 257;
|
||||
|
||||
// Largest alphabet size among all values listed above.
|
||||
static const uint LARGEST_TABLE_SIZE = 306;
|
||||
|
||||
enum {
|
||||
CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE,
|
||||
CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
enum FilterType {
|
||||
// These values must not be changed, because we use them directly
|
||||
// in RAR5 compression and decompression code.
|
||||
FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM,
|
||||
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE
|
||||
};
|
||||
|
||||
#endif
|
||||
352
unrar/consio.cpp
352
unrar/consio.cpp
@@ -1,352 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
#include "log.cpp"
|
||||
|
||||
static MESSAGE_TYPE MsgStream=MSG_STDOUT;
|
||||
static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
|
||||
|
||||
const int MaxMsgSize=2*NM+2048;
|
||||
|
||||
static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
static bool IsRedirected(DWORD nStdHandle)
|
||||
{
|
||||
HANDLE hStd=GetStdHandle(nStdHandle);
|
||||
DWORD Mode;
|
||||
return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void InitConsole()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We want messages like file names or progress percent to be printed
|
||||
// immediately. Use only in Windows, in Unix they can cause wprintf %ls
|
||||
// to fail with non-English strings.
|
||||
setbuf(stdout,NULL);
|
||||
setbuf(stderr,NULL);
|
||||
|
||||
// Detect if output is redirected and set output mode properly.
|
||||
// We do not want to send Unicode output to files and especially to pipes
|
||||
// like '|more', which cannot handle them correctly in Windows.
|
||||
// In Unix console output is UTF-8 and it is handled correctly
|
||||
// when redirecting, so no need to perform any adjustments.
|
||||
StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE);
|
||||
StderrRedirected=IsRedirected(STD_ERROR_HANDLE);
|
||||
StdinRedirected=IsRedirected(STD_INPUT_HANDLE);
|
||||
#ifdef _MSC_VER
|
||||
if (!StdoutRedirected)
|
||||
_setmode(_fileno(stdout), _O_U16TEXT);
|
||||
if (!StderrRedirected)
|
||||
_setmode(_fileno(stderr), _O_U16TEXT);
|
||||
#endif
|
||||
#elif defined(_UNIX)
|
||||
StdoutRedirected=!isatty(fileno(stdout));
|
||||
StderrRedirected=!isatty(fileno(stderr));
|
||||
StdinRedirected=!isatty(fileno(stdin));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void InitConsoleOptions(MESSAGE_TYPE MsgStream,RAR_CHARSET RedirectCharset)
|
||||
{
|
||||
::MsgStream=MsgStream;
|
||||
::RedirectCharset=RedirectCharset;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
|
||||
{
|
||||
// This buffer is for format string only, not for entire output,
|
||||
// so it can be short enough.
|
||||
wchar fmtw[1024];
|
||||
PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
|
||||
#ifdef _WIN_ALL
|
||||
safebuf wchar Msg[MaxMsgSize];
|
||||
if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
|
||||
{
|
||||
HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
|
||||
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
|
||||
DWORD Written;
|
||||
if (RedirectCharset==RCH_UNICODE)
|
||||
WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL);
|
||||
else
|
||||
{
|
||||
// Avoid Unicode for redirect in Windows, it does not work with pipes.
|
||||
safebuf char MsgA[MaxMsgSize];
|
||||
if (RedirectCharset==RCH_UTF8)
|
||||
WideToUtf(Msg,MsgA,ASIZE(MsgA));
|
||||
else
|
||||
WideToChar(Msg,MsgA,ASIZE(MsgA));
|
||||
if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
|
||||
CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding.
|
||||
|
||||
// We already converted \n to \r\n above, so we use WriteFile instead
|
||||
// of C library to avoid unnecessary additional conversion.
|
||||
WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// MSVC2008 vfwprintf writes every character to console separately
|
||||
// and it is too slow. We use direct WriteConsole call instead.
|
||||
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
|
||||
HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
|
||||
DWORD Written;
|
||||
WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL);
|
||||
#else
|
||||
vfwprintf(dest,fmtw,arglist);
|
||||
// We do not use setbuf(NULL) in Unix (see comments in InitConsole).
|
||||
fflush(dest);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void mprintf(const wchar *fmt,...)
|
||||
{
|
||||
if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
|
||||
return;
|
||||
|
||||
fflush(stderr); // Ensure proper message order.
|
||||
|
||||
va_list arglist;
|
||||
va_start(arglist,fmt);
|
||||
FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout;
|
||||
cvt_wprintf(dest,fmt,arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
void eprintf(const wchar *fmt,...)
|
||||
{
|
||||
if (MsgStream==MSG_NULL)
|
||||
return;
|
||||
|
||||
fflush(stdout); // Ensure proper message order.
|
||||
|
||||
va_list arglist;
|
||||
va_start(arglist,fmt);
|
||||
cvt_wprintf(stderr,fmt,arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
static void GetPasswordText(wchar *Str,uint MaxLength)
|
||||
{
|
||||
if (MaxLength==0)
|
||||
return;
|
||||
if (StdinRedirected)
|
||||
getwstr(Str,MaxLength); // Read from pipe or redirected file.
|
||||
else
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
|
||||
HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD ConInMode,ConOutMode;
|
||||
DWORD Read=0;
|
||||
GetConsoleMode(hConIn,&ConInMode);
|
||||
GetConsoleMode(hConOut,&ConOutMode);
|
||||
SetConsoleMode(hConIn,ENABLE_LINE_INPUT);
|
||||
SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
|
||||
ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
|
||||
Str[Read]=0;
|
||||
SetConsoleMode(hConIn,ConInMode);
|
||||
SetConsoleMode(hConOut,ConOutMode);
|
||||
#else
|
||||
char StrA[MAXPASSWORD];
|
||||
#if defined(_EMX) || defined (__VMS)
|
||||
fgets(StrA,ASIZE(StrA)-1,stdin);
|
||||
#elif defined(__sun)
|
||||
strncpyz(StrA,getpassphrase(""),ASIZE(StrA));
|
||||
#else
|
||||
strncpyz(StrA,getpass(""),ASIZE(StrA));
|
||||
#endif
|
||||
CharToWide(StrA,Str,MaxLength);
|
||||
cleandata(StrA,sizeof(StrA));
|
||||
#endif
|
||||
}
|
||||
Str[MaxLength-1]=0;
|
||||
RemoveLF(Str);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
|
||||
{
|
||||
if (!StdinRedirected)
|
||||
uiAlarm(UIALARM_QUESTION);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!StdinRedirected)
|
||||
if (Type==UIPASSWORD_GLOBAL)
|
||||
eprintf(L"\n%s: ",St(MAskPsw));
|
||||
else
|
||||
eprintf(St(MAskPswFor),FileName);
|
||||
|
||||
wchar PlainPsw[MAXPASSWORD];
|
||||
GetPasswordText(PlainPsw,ASIZE(PlainPsw));
|
||||
if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL)
|
||||
return false;
|
||||
if (!StdinRedirected && Type==UIPASSWORD_GLOBAL)
|
||||
{
|
||||
eprintf(St(MReAskPsw));
|
||||
wchar CmpStr[MAXPASSWORD];
|
||||
GetPasswordText(CmpStr,ASIZE(CmpStr));
|
||||
if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0)
|
||||
{
|
||||
eprintf(St(MNotMatchPsw));
|
||||
cleandata(PlainPsw,sizeof(PlainPsw));
|
||||
cleandata(CmpStr,sizeof(CmpStr));
|
||||
continue;
|
||||
}
|
||||
cleandata(CmpStr,sizeof(CmpStr));
|
||||
}
|
||||
Password->Set(PlainPsw);
|
||||
cleandata(PlainPsw,sizeof(PlainPsw));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
bool getwstr(wchar *str,size_t n)
|
||||
{
|
||||
// Print buffered prompt title function before waiting for input.
|
||||
fflush(stderr);
|
||||
|
||||
*str=0;
|
||||
#if defined(_WIN_ALL)
|
||||
// fgetws does not work well with non-English text in Windows,
|
||||
// so we do not use it.
|
||||
if (StdinRedirected) // ReadConsole does not work if redirected.
|
||||
{
|
||||
// fgets does not work well with pipes in Windows in our test.
|
||||
// Let's use files.
|
||||
Array<char> StrA(n*4); // Up to 4 UTF-8 characters per wchar_t.
|
||||
File SrcFile;
|
||||
SrcFile.SetHandleType(FILE_HANDLESTD);
|
||||
int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1);
|
||||
if (ReadSize<=0)
|
||||
{
|
||||
// Looks like stdin is a null device. We can enter to infinite loop
|
||||
// calling Ask(), so let's better exit.
|
||||
ErrHandler.Exit(RARX_USERBREAK);
|
||||
}
|
||||
StrA[ReadSize]=0;
|
||||
CharToWide(&StrA[0],str,n);
|
||||
cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords.
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD ReadSize=0;
|
||||
if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0)
|
||||
return false;
|
||||
str[ReadSize]=0;
|
||||
}
|
||||
#else
|
||||
if (fgetws(str,n,stdin)==NULL)
|
||||
ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
|
||||
#endif
|
||||
RemoveLF(str);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
// We allow this function to return 0 in case of invalid input,
|
||||
// because it might be convenient to press Enter to some not dangerous
|
||||
// prompts like "insert disk with next volume". We should call this function
|
||||
// again in case of 0 in dangerous prompt such as overwriting file.
|
||||
int Ask(const wchar *AskStr)
|
||||
{
|
||||
uiAlarm(UIALARM_QUESTION);
|
||||
|
||||
const int MaxItems=10;
|
||||
wchar Item[MaxItems][40];
|
||||
int ItemKeyPos[MaxItems],NumItems=0;
|
||||
|
||||
for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
|
||||
{
|
||||
wchar *CurItem=Item[NumItems];
|
||||
wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
|
||||
wchar *EndItem=wcschr(CurItem,'_');
|
||||
if (EndItem!=NULL)
|
||||
*EndItem=0;
|
||||
int KeyPos=0,CurKey;
|
||||
while ((CurKey=CurItem[KeyPos])!=0)
|
||||
{
|
||||
bool Found=false;
|
||||
for (int I=0;I<NumItems && !Found;I++)
|
||||
if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
|
||||
Found=true;
|
||||
if (!Found && CurKey!=' ')
|
||||
break;
|
||||
KeyPos++;
|
||||
}
|
||||
ItemKeyPos[NumItems]=KeyPos;
|
||||
NumItems++;
|
||||
}
|
||||
|
||||
for (int I=0;I<NumItems;I++)
|
||||
{
|
||||
eprintf(I==0 ? (NumItems>4 ? L"\n":L" "):L", ");
|
||||
int KeyPos=ItemKeyPos[I];
|
||||
for (int J=0;J<KeyPos;J++)
|
||||
eprintf(L"%c",Item[I][J]);
|
||||
eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]);
|
||||
}
|
||||
eprintf(L" ");
|
||||
wchar Str[50];
|
||||
getwstr(Str,ASIZE(Str));
|
||||
wchar Ch=toupperw(Str[0]);
|
||||
for (int I=0;I<NumItems;I++)
|
||||
if (Ch==Item[I][ItemKeyPos[I]])
|
||||
return I+1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static bool IsCommentUnsafe(const wchar *Data,size_t Size)
|
||||
{
|
||||
for (size_t I=0;I<Size;I++)
|
||||
if (Data[I]==27 && Data[I+1]=='[')
|
||||
for (size_t J=I+2;J<Size;J++)
|
||||
{
|
||||
// Return true for <ESC>[{key};"{string}"p used to redefine
|
||||
// a keyboard key on some terminals.
|
||||
if (Data[J]=='\"')
|
||||
return true;
|
||||
if (!IsDigit(Data[J]) && Data[J]!=';')
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void OutComment(const wchar *Comment,size_t Size)
|
||||
{
|
||||
if (IsCommentUnsafe(Comment,Size))
|
||||
return;
|
||||
const size_t MaxOutSize=0x400;
|
||||
for (size_t I=0;I<Size;I+=MaxOutSize)
|
||||
{
|
||||
wchar Msg[MaxOutSize+1];
|
||||
size_t CopySize=Min(MaxOutSize,Size-I);
|
||||
wcsncpy(Msg,Comment+I,CopySize);
|
||||
Msg[CopySize]=0;
|
||||
mprintf(L"%s",Msg);
|
||||
}
|
||||
mprintf(L"\n");
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef _RAR_CONSIO_
|
||||
#define _RAR_CONSIO_
|
||||
|
||||
void InitConsole();
|
||||
void InitConsoleOptions(MESSAGE_TYPE MsgStream,RAR_CHARSET RedirectCharset);
|
||||
void OutComment(const wchar *Comment,size_t Size);
|
||||
|
||||
#ifndef SILENT
|
||||
bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password);
|
||||
#endif
|
||||
|
||||
#ifdef SILENT
|
||||
inline void mprintf(const wchar *fmt,...) {}
|
||||
inline void eprintf(const wchar *fmt,...) {}
|
||||
inline void Alarm() {}
|
||||
inline int Ask(const wchar *AskStr) {return 0;}
|
||||
inline bool getwstr(wchar *str,size_t n) {return false;}
|
||||
#else
|
||||
void mprintf(const wchar *fmt,...);
|
||||
void eprintf(const wchar *fmt,...);
|
||||
void Alarm();
|
||||
int Ask(const wchar *AskStr);
|
||||
bool getwstr(wchar *str,size_t n);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
102
unrar/crc.cpp
102
unrar/crc.cpp
@@ -1,102 +0,0 @@
|
||||
// This CRC function is based on Intel Slicing-by-8 algorithm.
|
||||
//
|
||||
// Original Intel Slicing-by-8 code is available here:
|
||||
//
|
||||
// http://sourceforge.net/projects/slicing-by-8/
|
||||
//
|
||||
// Original Intel Slicing-by-8 code is licensed as:
|
||||
//
|
||||
// Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html
|
||||
|
||||
|
||||
#include "rar.hpp"
|
||||
|
||||
static uint crc_tables[8][256]; // Tables for Slicing-by-8.
|
||||
|
||||
|
||||
// Build the classic CRC32 lookup table.
|
||||
// We also provide this function to legacy RAR and ZIP decryption code.
|
||||
void InitCRC32(uint *CRCTab)
|
||||
{
|
||||
if (CRCTab[1]!=0)
|
||||
return;
|
||||
for (uint I=0;I<256;I++)
|
||||
{
|
||||
uint C=I;
|
||||
for (uint J=0;J<8;J++)
|
||||
C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
|
||||
CRCTab[I]=C;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void InitTables()
|
||||
{
|
||||
InitCRC32(crc_tables[0]);
|
||||
|
||||
for (uint I=0;I<256;I++) // Build additional lookup tables.
|
||||
{
|
||||
uint C=crc_tables[0][I];
|
||||
for (uint J=1;J<8;J++)
|
||||
{
|
||||
C=crc_tables[0][(byte)C]^(C>>8);
|
||||
crc_tables[J][I]=C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
|
||||
|
||||
uint CRC32(uint StartCRC,const void *Addr,size_t Size)
|
||||
{
|
||||
byte *Data=(byte *)Addr;
|
||||
|
||||
// Align Data to 8 for better performance.
|
||||
for (;Size>0 && ((size_t)Data & 7);Size--,Data++)
|
||||
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
|
||||
|
||||
for (;Size>=8;Size-=8,Data+=8)
|
||||
{
|
||||
#ifdef BIG_ENDIAN
|
||||
StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24);
|
||||
uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24);
|
||||
#else
|
||||
StartCRC ^= *(uint32 *) Data;
|
||||
uint NextData = *(uint32 *) (Data +4);
|
||||
#endif
|
||||
StartCRC = crc_tables[7][(byte) StartCRC ] ^
|
||||
crc_tables[6][(byte)(StartCRC >> 8) ] ^
|
||||
crc_tables[5][(byte)(StartCRC >> 16)] ^
|
||||
crc_tables[4][(byte)(StartCRC >> 24)] ^
|
||||
crc_tables[3][(byte) NextData ] ^
|
||||
crc_tables[2][(byte)(NextData >>8 ) ] ^
|
||||
crc_tables[1][(byte)(NextData >> 16)] ^
|
||||
crc_tables[0][(byte)(NextData >> 24)];
|
||||
}
|
||||
|
||||
for (;Size>0;Size--,Data++) // Process left data.
|
||||
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
|
||||
|
||||
return StartCRC;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// For RAR 1.4 archives in case somebody still has them.
|
||||
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
|
||||
{
|
||||
byte *Data=(byte *)Addr;
|
||||
for (size_t I=0;I<Size;I++)
|
||||
{
|
||||
StartCRC=(StartCRC+Data[I])&0xffff;
|
||||
StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff;
|
||||
}
|
||||
return StartCRC;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef _RAR_CRC_
|
||||
#define _RAR_CRC_
|
||||
|
||||
// This function is only to intialize external CRC tables. We do not need to
|
||||
// call it before calculating CRC32.
|
||||
void InitCRC32(uint *CRCTab);
|
||||
|
||||
uint CRC32(uint StartCRC,const void *Addr,size_t Size);
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
134
unrar/crypt.cpp
134
unrar/crypt.cpp
@@ -1,134 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
#include "crypt1.cpp"
|
||||
#include "crypt2.cpp"
|
||||
#endif
|
||||
#include "crypt3.cpp"
|
||||
#include "crypt5.cpp"
|
||||
|
||||
|
||||
CryptData::CryptData()
|
||||
{
|
||||
Method=CRYPT_NONE;
|
||||
memset(KDF3Cache,0,sizeof(KDF3Cache));
|
||||
memset(KDF5Cache,0,sizeof(KDF5Cache));
|
||||
KDF3CachePos=0;
|
||||
KDF5CachePos=0;
|
||||
memset(CRCTab,0,sizeof(CRCTab));
|
||||
}
|
||||
|
||||
|
||||
CryptData::~CryptData()
|
||||
{
|
||||
cleandata(KDF3Cache,sizeof(KDF3Cache));
|
||||
cleandata(KDF5Cache,sizeof(KDF5Cache));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CryptData::DecryptBlock(byte *Buf,size_t Size)
|
||||
{
|
||||
switch(Method)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
case CRYPT_RAR13:
|
||||
Decrypt13(Buf,Size);
|
||||
break;
|
||||
case CRYPT_RAR15:
|
||||
Crypt15(Buf,Size);
|
||||
break;
|
||||
case CRYPT_RAR20:
|
||||
for (size_t I=0;I<Size;I+=CRYPT_BLOCK_SIZE)
|
||||
DecryptBlock20(Buf+I);
|
||||
break;
|
||||
#endif
|
||||
case CRYPT_RAR30:
|
||||
case CRYPT_RAR50:
|
||||
rin.blockDecrypt(Buf,Size,Buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
|
||||
SecPassword *Password,const byte *Salt,
|
||||
const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
|
||||
{
|
||||
if (!Password->IsSet() || Method==CRYPT_NONE)
|
||||
return false;
|
||||
|
||||
CryptData::Method=Method;
|
||||
|
||||
wchar PwdW[MAXPASSWORD];
|
||||
Password->Get(PwdW,ASIZE(PwdW));
|
||||
char PwdA[MAXPASSWORD];
|
||||
WideToChar(PwdW,PwdA,ASIZE(PwdA));
|
||||
|
||||
switch(Method)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
case CRYPT_RAR13:
|
||||
SetKey13(PwdA);
|
||||
break;
|
||||
case CRYPT_RAR15:
|
||||
SetKey15(PwdA);
|
||||
break;
|
||||
case CRYPT_RAR20:
|
||||
SetKey20(PwdA);
|
||||
break;
|
||||
#endif
|
||||
case CRYPT_RAR30:
|
||||
SetKey30(Encrypt,Password,PwdW,Salt);
|
||||
break;
|
||||
case CRYPT_RAR50:
|
||||
SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
|
||||
break;
|
||||
}
|
||||
cleandata(PwdA,sizeof(PwdA));
|
||||
cleandata(PwdW,sizeof(PwdW));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Use the current system time to additionally randomize data.
|
||||
static void TimeRandomize(byte *RndBuf,size_t BufSize)
|
||||
{
|
||||
static uint Count=0;
|
||||
RarTime CurTime;
|
||||
CurTime.SetCurrentTime();
|
||||
uint64 Random=CurTime.GetWin()+clock();
|
||||
for (size_t I=0;I<BufSize;I++)
|
||||
{
|
||||
byte RndByte = byte (Random >> ( (I & 7) * 8 ));
|
||||
RndBuf[I]=byte( (RndByte ^ I) + Count++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Fill buffer with random data.
|
||||
void GetRnd(byte *RndBuf,size_t BufSize)
|
||||
{
|
||||
bool Success=false;
|
||||
#if defined(_WIN_ALL)
|
||||
HCRYPTPROV hProvider = 0;
|
||||
if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
||||
{
|
||||
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE;
|
||||
CryptReleaseContext(hProvider, 0);
|
||||
}
|
||||
#elif defined(_UNIX)
|
||||
FILE *rndf = fopen("/dev/urandom", "r");
|
||||
if (rndf!=NULL)
|
||||
{
|
||||
Success=fread(RndBuf, BufSize, 1, rndf) == BufSize;
|
||||
fclose(rndf);
|
||||
}
|
||||
#endif
|
||||
// We use this code only as the last resort if code above failed.
|
||||
if (!Success)
|
||||
TimeRandomize(RndBuf,BufSize);
|
||||
}
|
||||
101
unrar/crypt.hpp
101
unrar/crypt.hpp
@@ -1,101 +0,0 @@
|
||||
#ifndef _RAR_CRYPT_
|
||||
#define _RAR_CRYPT_
|
||||
|
||||
|
||||
enum CRYPT_METHOD {
|
||||
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50
|
||||
};
|
||||
|
||||
#define SIZE_SALT50 16
|
||||
#define SIZE_SALT30 8
|
||||
#define SIZE_INITV 16
|
||||
#define SIZE_PSWCHECK 8
|
||||
#define SIZE_PSWCHECK_CSUM 4
|
||||
|
||||
#define CRYPT_BLOCK_SIZE 16
|
||||
#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf
|
||||
|
||||
#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
|
||||
#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
|
||||
#define CRYPT_VERSION 0 // Supported encryption version.
|
||||
|
||||
|
||||
class CryptData
|
||||
{
|
||||
struct KDF5CacheItem
|
||||
{
|
||||
SecPassword Pwd;
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte Key[32];
|
||||
uint Lg2Count; // Log2 of PBKDF2 repetition count.
|
||||
byte PswCheckValue[SHA256_DIGEST_SIZE];
|
||||
byte HashKeyValue[SHA256_DIGEST_SIZE];
|
||||
};
|
||||
|
||||
struct KDF3CacheItem
|
||||
{
|
||||
SecPassword Pwd;
|
||||
byte Salt[SIZE_SALT30];
|
||||
byte Key[16];
|
||||
byte Init[16];
|
||||
bool SaltPresent;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
void SetKey13(const char *Password);
|
||||
void Decrypt13(byte *Data,size_t Count);
|
||||
|
||||
void SetKey15(const char *Password);
|
||||
void Crypt15(byte *Data,size_t Count);
|
||||
|
||||
void SetKey20(const char *Password);
|
||||
void Swap20(byte *Ch1,byte *Ch2);
|
||||
void UpdKeys20(byte *Buf);
|
||||
void EncryptBlock20(byte *Buf);
|
||||
void DecryptBlock20(byte *Buf);
|
||||
|
||||
void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
|
||||
void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
|
||||
|
||||
KDF3CacheItem KDF3Cache[4];
|
||||
uint KDF3CachePos;
|
||||
|
||||
KDF5CacheItem KDF5Cache[4];
|
||||
uint KDF5CachePos;
|
||||
|
||||
CRYPT_METHOD Method;
|
||||
|
||||
Rijndael rin;
|
||||
|
||||
uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption.
|
||||
|
||||
byte SubstTable20[256];
|
||||
uint Key20[4];
|
||||
|
||||
byte Key13[3];
|
||||
ushort Key15[4];
|
||||
public:
|
||||
CryptData();
|
||||
~CryptData();
|
||||
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
|
||||
const byte *Salt,const byte *InitV,uint Lg2Cnt,
|
||||
byte *HashKey,byte *PswCheck);
|
||||
void SetAV15Encryption();
|
||||
void SetCmt13Encryption();
|
||||
void EncryptBlock(byte *Buf,size_t Size);
|
||||
void DecryptBlock(byte *Buf,size_t Size);
|
||||
static void SetSalt(byte *Salt,size_t SaltSize);
|
||||
};
|
||||
|
||||
void GetRnd(byte *RndBuf,size_t BufSize);
|
||||
|
||||
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
|
||||
size_t DataLength,byte *ResDigest);
|
||||
void pbkdf2(const byte *pass, size_t pass_len, const byte *salt,
|
||||
size_t salt_len,byte *key, byte *Value1, byte *Value2,
|
||||
uint rounds);
|
||||
|
||||
void ConvertHashToMAC(HashValue *Value,byte *Key);
|
||||
|
||||
#endif
|
||||
@@ -1,79 +0,0 @@
|
||||
extern uint CRCTab[256];
|
||||
|
||||
void CryptData::SetKey13(const char *Password)
|
||||
{
|
||||
Key13[0]=Key13[1]=Key13[2]=0;
|
||||
for (size_t I=0;Password[I]!=0;I++)
|
||||
{
|
||||
byte P=Password[I];
|
||||
Key13[0]+=P;
|
||||
Key13[1]^=P;
|
||||
Key13[2]+=P;
|
||||
Key13[2]=(byte)rotls(Key13[2],1,8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::SetKey15(const char *Password)
|
||||
{
|
||||
InitCRC32(CRCTab);
|
||||
uint PswCRC=CRC32(0xffffffff,Password,strlen(Password));
|
||||
Key15[0]=PswCRC&0xffff;
|
||||
Key15[1]=(PswCRC>>16)&0xffff;
|
||||
Key15[2]=Key15[3]=0;
|
||||
for (size_t I=0;Password[I]!=0;I++)
|
||||
{
|
||||
byte P=Password[I];
|
||||
Key15[2]^=P^CRCTab[P];
|
||||
Key15[3]+=P+(CRCTab[P]>>16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::SetAV15Encryption()
|
||||
{
|
||||
InitCRC32(CRCTab);
|
||||
Method=CRYPT_RAR15;
|
||||
Key15[0]=0x4765;
|
||||
Key15[1]=0x9021;
|
||||
Key15[2]=0x7382;
|
||||
Key15[3]=0x5215;
|
||||
}
|
||||
|
||||
|
||||
void CryptData::SetCmt13Encryption()
|
||||
{
|
||||
Method=CRYPT_RAR13;
|
||||
Key13[0]=0;
|
||||
Key13[1]=7;
|
||||
Key13[2]=77;
|
||||
}
|
||||
|
||||
|
||||
void CryptData::Decrypt13(byte *Data,size_t Count)
|
||||
{
|
||||
while (Count--)
|
||||
{
|
||||
Key13[1]+=Key13[2];
|
||||
Key13[0]+=Key13[1];
|
||||
*Data-=Key13[0];
|
||||
Data++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::Crypt15(byte *Data,size_t Count)
|
||||
{
|
||||
while (Count--)
|
||||
{
|
||||
Key15[0]+=0x1234;
|
||||
Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
|
||||
Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16;
|
||||
Key15[0]^=Key15[2];
|
||||
Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1];
|
||||
Key15[3]=rotrs(Key15[3]&0xffff,1,16);
|
||||
Key15[0]^=Key15[3];
|
||||
*Data^=(byte)(Key15[0]>>8);
|
||||
Data++;
|
||||
}
|
||||
}
|
||||
133
unrar/crypt2.cpp
133
unrar/crypt2.cpp
@@ -1,133 +0,0 @@
|
||||
#define NROUNDS 32
|
||||
|
||||
#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \
|
||||
((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \
|
||||
((uint)SubstTable20[(int)(t>>16)&255]<<16) | \
|
||||
((uint)SubstTable20[(int)(t>>24)&255]<<24) )
|
||||
|
||||
|
||||
static byte InitSubstTable20[256]={
|
||||
215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
|
||||
232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
|
||||
255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
|
||||
71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
|
||||
107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
|
||||
158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
|
||||
97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
|
||||
164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
|
||||
207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
|
||||
122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
|
||||
131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
|
||||
224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
|
||||
118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
|
||||
161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
|
||||
0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
|
||||
116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
|
||||
};
|
||||
|
||||
|
||||
void CryptData::SetKey20(const char *Password)
|
||||
{
|
||||
InitCRC32(CRCTab);
|
||||
|
||||
char Psw[MAXPASSWORD];
|
||||
strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below.
|
||||
size_t PswLength=strlen(Psw);
|
||||
|
||||
Key20[0]=0xD3A3B879L;
|
||||
Key20[1]=0x3F6D12F7L;
|
||||
Key20[2]=0x7515A235L;
|
||||
Key20[3]=0xA4E7F123L;
|
||||
|
||||
memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20));
|
||||
for (uint J=0;J<256;J++)
|
||||
for (size_t I=0;I<PswLength;I+=2)
|
||||
{
|
||||
uint N1=(byte)CRCTab [ (byte(Password[I]) - J) &0xff];
|
||||
uint N2=(byte)CRCTab [ (byte(Password[I+1]) + J) &0xff];
|
||||
for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++)
|
||||
Swap20(&SubstTable20[N1],&SubstTable20[(N1+I+K)&0xff]);
|
||||
}
|
||||
|
||||
// Incomplete last block of password must be zero padded.
|
||||
if ((PswLength & CRYPT_BLOCK_MASK)!=0)
|
||||
for (size_t I=PswLength;I<=(PswLength|CRYPT_BLOCK_MASK);I++)
|
||||
Psw[I]=0;
|
||||
|
||||
for (size_t I=0;I<PswLength;I+=CRYPT_BLOCK_SIZE)
|
||||
EncryptBlock20((byte *)Psw+I);
|
||||
}
|
||||
|
||||
|
||||
void CryptData::EncryptBlock20(byte *Buf)
|
||||
{
|
||||
uint A,B,C,D,T,TA,TB;
|
||||
A=RawGet4(Buf+0)^Key20[0];
|
||||
B=RawGet4(Buf+4)^Key20[1];
|
||||
C=RawGet4(Buf+8)^Key20[2];
|
||||
D=RawGet4(Buf+12)^Key20[3];
|
||||
for(int I=0;I<NROUNDS;I++)
|
||||
{
|
||||
T=((C+rotls(D,11,32))^Key20[I&3]);
|
||||
TA=A^substLong(T);
|
||||
T=((D^rotls(C,17,32))+Key20[I&3]);
|
||||
TB=B^substLong(T);
|
||||
A=C;
|
||||
B=D;
|
||||
C=TA;
|
||||
D=TB;
|
||||
}
|
||||
RawPut4(C^Key20[0],Buf+0);
|
||||
RawPut4(D^Key20[1],Buf+4);
|
||||
RawPut4(A^Key20[2],Buf+8);
|
||||
RawPut4(B^Key20[3],Buf+12);
|
||||
UpdKeys20(Buf);
|
||||
}
|
||||
|
||||
|
||||
void CryptData::DecryptBlock20(byte *Buf)
|
||||
{
|
||||
byte InBuf[16];
|
||||
uint A,B,C,D,T,TA,TB;
|
||||
A=RawGet4(Buf+0)^Key20[0];
|
||||
B=RawGet4(Buf+4)^Key20[1];
|
||||
C=RawGet4(Buf+8)^Key20[2];
|
||||
D=RawGet4(Buf+12)^Key20[3];
|
||||
memcpy(InBuf,Buf,sizeof(InBuf));
|
||||
for(int I=NROUNDS-1;I>=0;I--)
|
||||
{
|
||||
T=((C+rotls(D,11,32))^Key20[I&3]);
|
||||
TA=A^substLong(T);
|
||||
T=((D^rotls(C,17,32))+Key20[I&3]);
|
||||
TB=B^substLong(T);
|
||||
A=C;
|
||||
B=D;
|
||||
C=TA;
|
||||
D=TB;
|
||||
}
|
||||
RawPut4(C^Key20[0],Buf+0);
|
||||
RawPut4(D^Key20[1],Buf+4);
|
||||
RawPut4(A^Key20[2],Buf+8);
|
||||
RawPut4(B^Key20[3],Buf+12);
|
||||
UpdKeys20(InBuf);
|
||||
}
|
||||
|
||||
|
||||
void CryptData::UpdKeys20(byte *Buf)
|
||||
{
|
||||
for (int I=0;I<16;I+=4)
|
||||
{
|
||||
Key20[0]^=CRCTab[Buf[I]];
|
||||
Key20[1]^=CRCTab[Buf[I+1]];
|
||||
Key20[2]^=CRCTab[Buf[I+2]];
|
||||
Key20[3]^=CRCTab[Buf[I+3]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::Swap20(byte *Ch1,byte *Ch2)
|
||||
{
|
||||
byte Ch=*Ch1;
|
||||
*Ch1=*Ch2;
|
||||
*Ch2=Ch;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt)
|
||||
{
|
||||
byte AESKey[16],AESInit[16];
|
||||
|
||||
bool Cached=false;
|
||||
for (uint I=0;I<ASIZE(KDF3Cache);I++)
|
||||
if (KDF3Cache[I].Pwd==*Password &&
|
||||
(Salt==NULL && !KDF3Cache[I].SaltPresent || Salt!=NULL &&
|
||||
KDF3Cache[I].SaltPresent && memcmp(KDF3Cache[I].Salt,Salt,SIZE_SALT30)==0))
|
||||
{
|
||||
memcpy(AESKey,KDF3Cache[I].Key,sizeof(AESKey));
|
||||
SecHideData(AESKey,sizeof(AESKey),false,false);
|
||||
memcpy(AESInit,KDF3Cache[I].Init,sizeof(AESInit));
|
||||
Cached=true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Cached)
|
||||
{
|
||||
byte RawPsw[2*MAXPASSWORD+SIZE_SALT30];
|
||||
WideToRaw(PwdW,RawPsw,ASIZE(RawPsw));
|
||||
size_t RawLength=2*wcslen(PwdW);
|
||||
if (Salt!=NULL)
|
||||
{
|
||||
memcpy(RawPsw+RawLength,Salt,SIZE_SALT30);
|
||||
RawLength+=SIZE_SALT30;
|
||||
}
|
||||
sha1_context c;
|
||||
sha1_init(&c);
|
||||
|
||||
const int HashRounds=0x40000;
|
||||
for (int I=0;I<HashRounds;I++)
|
||||
{
|
||||
sha1_process_rar29( &c, RawPsw, RawLength );
|
||||
byte PswNum[3];
|
||||
PswNum[0]=(byte)I;
|
||||
PswNum[1]=(byte)(I>>8);
|
||||
PswNum[2]=(byte)(I>>16);
|
||||
sha1_process(&c, PswNum, 3);
|
||||
if (I%(HashRounds/16)==0)
|
||||
{
|
||||
sha1_context tempc=c;
|
||||
uint32 digest[5];
|
||||
sha1_done( &tempc, digest );
|
||||
AESInit[I/(HashRounds/16)]=(byte)digest[4];
|
||||
}
|
||||
}
|
||||
uint32 digest[5];
|
||||
sha1_done( &c, digest );
|
||||
for (int I=0;I<4;I++)
|
||||
for (int J=0;J<4;J++)
|
||||
AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
|
||||
|
||||
KDF3Cache[KDF3CachePos].Pwd=*Password;
|
||||
if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true)
|
||||
memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30);
|
||||
memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey));
|
||||
SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false);
|
||||
memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit));
|
||||
KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache);
|
||||
|
||||
cleandata(RawPsw,sizeof(RawPsw));
|
||||
}
|
||||
rin.Init(Encrypt, AESKey, 128, AESInit);
|
||||
cleandata(AESKey,sizeof(AESKey));
|
||||
cleandata(AESInit,sizeof(AESInit));
|
||||
}
|
||||
|
||||
233
unrar/crypt5.cpp
233
unrar/crypt5.cpp
@@ -1,233 +0,0 @@
|
||||
static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
|
||||
size_t DataLength,byte *ResDigest,
|
||||
sha256_context *ICtxOpt,bool *SetIOpt,
|
||||
sha256_context *RCtxOpt,bool *SetROpt)
|
||||
{
|
||||
const size_t Sha256BlockSize=64; // As defined in RFC 4868.
|
||||
|
||||
byte KeyHash[SHA256_DIGEST_SIZE];
|
||||
if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash.
|
||||
{
|
||||
sha256_context KCtx;
|
||||
sha256_init(&KCtx);
|
||||
sha256_process(&KCtx, Key, KeyLength);
|
||||
sha256_done(&KCtx, KeyHash);
|
||||
|
||||
Key = KeyHash;
|
||||
KeyLength = SHA256_DIGEST_SIZE;
|
||||
}
|
||||
|
||||
byte KeyBuf[Sha256BlockSize]; // Store the padded key here.
|
||||
sha256_context ICtx;
|
||||
|
||||
if (ICtxOpt!=NULL && *SetIOpt)
|
||||
ICtx=*ICtxOpt; // Use already calculated first block context.
|
||||
else
|
||||
{
|
||||
// This calculation is the same for all iterations with same password.
|
||||
// So for PBKDF2 we can calculate it only for first block and then reuse
|
||||
// to improve performance.
|
||||
|
||||
for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest.
|
||||
KeyBuf[I] = Key[I] ^ 0x36;
|
||||
for (size_t I = KeyLength; I < Sha256BlockSize; I++)
|
||||
KeyBuf[I] = 0x36;
|
||||
|
||||
sha256_init(&ICtx);
|
||||
sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key.
|
||||
}
|
||||
|
||||
if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse.
|
||||
{
|
||||
*ICtxOpt=ICtx;
|
||||
*SetIOpt=true;
|
||||
}
|
||||
|
||||
sha256_process(&ICtx, Data, DataLength); // Hash data.
|
||||
|
||||
byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data.
|
||||
sha256_done(&ICtx, IDig);
|
||||
|
||||
sha256_context RCtx;
|
||||
|
||||
if (RCtxOpt!=NULL && *SetROpt)
|
||||
RCtx=*RCtxOpt; // Use already calculated first block context.
|
||||
else
|
||||
{
|
||||
// This calculation is the same for all iterations with same password.
|
||||
// So for PBKDF2 we can calculate it only for first block and then reuse
|
||||
// to improve performance.
|
||||
|
||||
for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding.
|
||||
KeyBuf[I] = Key[I] ^ 0x5c;
|
||||
for (size_t I = KeyLength; I < Sha256BlockSize; I++)
|
||||
KeyBuf[I] = 0x5c;
|
||||
|
||||
sha256_init(&RCtx);
|
||||
sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key.
|
||||
}
|
||||
|
||||
if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse.
|
||||
{
|
||||
*RCtxOpt=RCtx;
|
||||
*SetROpt=true;
|
||||
}
|
||||
|
||||
sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest.
|
||||
|
||||
sha256_done(&RCtx, ResDigest);
|
||||
}
|
||||
|
||||
|
||||
// PBKDF2 for 32 byte key length. We generate the key for specified number
|
||||
// of iteration count also as two supplementary values (key for checksums
|
||||
// and password verification) for iterations+16 and iterations+32.
|
||||
void pbkdf2(const byte *Pwd, size_t PwdLength,
|
||||
const byte *Salt, size_t SaltLength,
|
||||
byte *Key, byte *V1, byte *V2, uint Count)
|
||||
{
|
||||
const size_t MaxSalt=64;
|
||||
byte SaltData[MaxSalt+4];
|
||||
memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
|
||||
|
||||
SaltData[SaltLength + 0] = 0; // Salt concatenated to 1.
|
||||
SaltData[SaltLength + 1] = 0;
|
||||
SaltData[SaltLength + 2] = 0;
|
||||
SaltData[SaltLength + 3] = 1;
|
||||
|
||||
// First iteration: HMAC of password, salt and block index (1).
|
||||
byte U1[SHA256_DIGEST_SIZE];
|
||||
hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL);
|
||||
byte Fn[SHA256_DIGEST_SIZE]; // Current function value.
|
||||
memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration.
|
||||
|
||||
uint CurCount[] = { Count-1, 16, 16 };
|
||||
byte *CurValue[] = { Key , V1, V2 };
|
||||
|
||||
sha256_context ICtxOpt,RCtxOpt;
|
||||
bool SetIOpt=false,SetROpt=false;
|
||||
|
||||
byte U2[SHA256_DIGEST_SIZE];
|
||||
for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values.
|
||||
{
|
||||
for (uint J = 0; J < CurCount[I]; J++)
|
||||
{
|
||||
// U2 = PRF (P, U1).
|
||||
hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt);
|
||||
memcpy(U1, U2, sizeof(U1));
|
||||
for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U.
|
||||
Fn[K] ^= U1[K];
|
||||
}
|
||||
memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
cleandata(SaltData, sizeof(SaltData));
|
||||
cleandata(Fn, sizeof(Fn));
|
||||
cleandata(U1, sizeof(U1));
|
||||
cleandata(U2, sizeof(U2));
|
||||
}
|
||||
|
||||
|
||||
void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
|
||||
const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
|
||||
byte *PswCheck)
|
||||
{
|
||||
if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
|
||||
return;
|
||||
|
||||
byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
|
||||
bool Found=false;
|
||||
for (uint I=0;I<ASIZE(KDF5Cache);I++)
|
||||
{
|
||||
KDF5CacheItem *Item=KDF5Cache+I;
|
||||
if (Item->Lg2Count==Lg2Cnt && Item->Pwd==*Password &&
|
||||
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
|
||||
{
|
||||
memcpy(Key,Item->Key,sizeof(Key));
|
||||
SecHideData(Key,sizeof(Key),false,false);
|
||||
|
||||
memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue));
|
||||
memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue));
|
||||
Found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Found)
|
||||
{
|
||||
char PwdUtf[MAXPASSWORD*4];
|
||||
WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf));
|
||||
|
||||
pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<<Lg2Cnt));
|
||||
cleandata(PwdUtf,sizeof(PwdUtf));
|
||||
|
||||
KDF5CacheItem *Item=KDF5Cache+(KDF5CachePos++ % ASIZE(KDF5Cache));
|
||||
Item->Lg2Count=Lg2Cnt;
|
||||
Item->Pwd=*Password;
|
||||
memcpy(Item->Salt,Salt,SIZE_SALT50);
|
||||
memcpy(Item->Key,Key,sizeof(Item->Key));
|
||||
memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue));
|
||||
memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue));
|
||||
SecHideData(Item->Key,sizeof(Item->Key),true,false);
|
||||
}
|
||||
if (HashKey!=NULL)
|
||||
memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE);
|
||||
if (PswCheck!=NULL)
|
||||
{
|
||||
memset(PswCheck,0,SIZE_PSWCHECK);
|
||||
for (uint I=0;I<SHA256_DIGEST_SIZE;I++)
|
||||
PswCheck[I%SIZE_PSWCHECK]^=PswCheckValue[I];
|
||||
cleandata(PswCheckValue,sizeof(PswCheckValue));
|
||||
}
|
||||
|
||||
// NULL initialization vector is possible if we only need the password
|
||||
// check value for archive encryption header.
|
||||
if (InitV!=NULL)
|
||||
rin.Init(Encrypt, Key, 256, InitV);
|
||||
|
||||
cleandata(Key,sizeof(Key));
|
||||
}
|
||||
|
||||
|
||||
void ConvertHashToMAC(HashValue *Value,byte *Key)
|
||||
{
|
||||
if (Value->Type==HASH_CRC32)
|
||||
{
|
||||
byte RawCRC[4];
|
||||
RawPut4(Value->CRC32,RawCRC);
|
||||
byte Digest[SHA256_DIGEST_SIZE];
|
||||
hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL);
|
||||
Value->CRC32=0;
|
||||
for (uint I=0;I<ASIZE(Digest);I++)
|
||||
Value->CRC32^=Digest[I] << ((I & 3) * 8);
|
||||
}
|
||||
if (Value->Type==HASH_BLAKE2)
|
||||
{
|
||||
byte Digest[BLAKE2_DIGEST_SIZE];
|
||||
hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL);
|
||||
memcpy(Value->Digest,Digest,sizeof(Value->Digest));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void TestPBKDF2();
|
||||
struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF;
|
||||
|
||||
void TestPBKDF2() // Test PBKDF2 HMAC-SHA256
|
||||
{
|
||||
byte Key[32],V1[32],V2[32];
|
||||
|
||||
pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1);
|
||||
byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b };
|
||||
mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed");
|
||||
|
||||
pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096);
|
||||
byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a };
|
||||
mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed");
|
||||
|
||||
pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536);
|
||||
byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf};
|
||||
mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed");
|
||||
}
|
||||
#endif
|
||||
474
unrar/dll.cpp
474
unrar/dll.cpp
@@ -1,474 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
static int RarErrorToDll(RAR_EXIT ErrCode);
|
||||
|
||||
struct DataSet
|
||||
{
|
||||
CommandData Cmd;
|
||||
Archive Arc;
|
||||
CmdExtract Extract;
|
||||
int OpenMode;
|
||||
int HeaderSize;
|
||||
|
||||
DataSet():Arc(&Cmd),Extract(&Cmd) {};
|
||||
};
|
||||
|
||||
|
||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
|
||||
{
|
||||
RAROpenArchiveDataEx rx;
|
||||
memset(&rx,0,sizeof(rx));
|
||||
rx.ArcName=r->ArcName;
|
||||
rx.OpenMode=r->OpenMode;
|
||||
rx.CmtBuf=r->CmtBuf;
|
||||
rx.CmtBufSize=r->CmtBufSize;
|
||||
HANDLE hArc=RAROpenArchiveEx(&rx);
|
||||
r->OpenResult=rx.OpenResult;
|
||||
r->CmtSize=rx.CmtSize;
|
||||
r->CmtState=rx.CmtState;
|
||||
return hArc;
|
||||
}
|
||||
|
||||
|
||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
|
||||
{
|
||||
DataSet *Data=NULL;
|
||||
try
|
||||
{
|
||||
r->OpenResult=0;
|
||||
Data=new DataSet;
|
||||
Data->Cmd.DllError=0;
|
||||
Data->OpenMode=r->OpenMode;
|
||||
Data->Cmd.FileArgs.AddString(L"*");
|
||||
|
||||
char AnsiArcName[NM];
|
||||
*AnsiArcName=0;
|
||||
if (r->ArcName!=NULL)
|
||||
{
|
||||
strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName));
|
||||
#ifdef _WIN_ALL
|
||||
if (!AreFileApisANSI())
|
||||
{
|
||||
OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName));
|
||||
AnsiArcName[ASIZE(AnsiArcName)-1]=0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
wchar ArcName[NM];
|
||||
GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName));
|
||||
|
||||
Data->Cmd.AddArcName(ArcName);
|
||||
Data->Cmd.Overwrite=OVERWRITE_ALL;
|
||||
Data->Cmd.VersionControl=1;
|
||||
|
||||
Data->Cmd.Callback=r->Callback;
|
||||
Data->Cmd.UserData=r->UserData;
|
||||
|
||||
// Open shared mode is added by request of dll users, who need to
|
||||
// browse and unpack archives while downloading.
|
||||
Data->Cmd.OpenShared = true;
|
||||
if (!Data->Arc.Open(ArcName,FMF_OPENSHARED))
|
||||
{
|
||||
r->OpenResult=ERAR_EOPEN;
|
||||
delete Data;
|
||||
return NULL;
|
||||
}
|
||||
if (!Data->Arc.IsArchive(true))
|
||||
{
|
||||
if (Data->Cmd.DllError!=0)
|
||||
r->OpenResult=Data->Cmd.DllError;
|
||||
else
|
||||
{
|
||||
RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
|
||||
if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING)
|
||||
r->OpenResult=RarErrorToDll(ErrCode);
|
||||
else
|
||||
r->OpenResult=ERAR_BAD_ARCHIVE;
|
||||
}
|
||||
delete Data;
|
||||
return NULL;
|
||||
}
|
||||
r->Flags=0;
|
||||
|
||||
if (Data->Arc.Volume)
|
||||
r->Flags|=0x01;
|
||||
if (Data->Arc.Locked)
|
||||
r->Flags|=0x04;
|
||||
if (Data->Arc.Solid)
|
||||
r->Flags|=0x08;
|
||||
if (Data->Arc.NewNumbering)
|
||||
r->Flags|=0x10;
|
||||
if (Data->Arc.Signed)
|
||||
r->Flags|=0x20;
|
||||
if (Data->Arc.Protected)
|
||||
r->Flags|=0x40;
|
||||
if (Data->Arc.Encrypted)
|
||||
r->Flags|=0x80;
|
||||
if (Data->Arc.FirstVolume)
|
||||
r->Flags|=0x100;
|
||||
|
||||
Array<wchar> CmtDataW;
|
||||
if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
|
||||
{
|
||||
Array<char> CmtData(CmtDataW.Size()*4+1);
|
||||
memset(&CmtData[0],0,CmtData.Size());
|
||||
WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
|
||||
size_t Size=strlen(&CmtData[0])+1;
|
||||
|
||||
r->Flags|=2;
|
||||
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
|
||||
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
|
||||
memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
|
||||
if (Size<=r->CmtBufSize)
|
||||
r->CmtBuf[r->CmtSize-1]=0;
|
||||
}
|
||||
else
|
||||
r->CmtState=r->CmtSize=0;
|
||||
Data->Extract.ExtractArchiveInit(Data->Arc);
|
||||
return (HANDLE)Data;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
if (Data!=NULL && Data->Cmd.DllError!=0)
|
||||
r->OpenResult=Data->Cmd.DllError;
|
||||
else
|
||||
r->OpenResult=RarErrorToDll(ErrCode);
|
||||
if (Data != NULL)
|
||||
delete Data;
|
||||
return NULL;
|
||||
}
|
||||
catch (std::bad_alloc&) // Catch 'new' exception.
|
||||
{
|
||||
r->OpenResult=ERAR_NO_MEMORY;
|
||||
if (Data != NULL)
|
||||
delete Data;
|
||||
}
|
||||
return NULL; // To make compilers happy.
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARCloseArchive(HANDLE hArcData)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
bool Success=Data==NULL ? false:Data->Arc.Close();
|
||||
delete Data;
|
||||
return Success ? ERAR_SUCCESS : ERAR_ECLOSE;
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
|
||||
{
|
||||
struct RARHeaderDataEx X;
|
||||
memset(&X,0,sizeof(X));
|
||||
|
||||
int Code=RARReadHeaderEx(hArcData,&X);
|
||||
|
||||
strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName));
|
||||
strncpyz(D->FileName,X.FileName,ASIZE(D->FileName));
|
||||
D->Flags=X.Flags;
|
||||
D->PackSize=X.PackSize;
|
||||
D->UnpSize=X.UnpSize;
|
||||
D->HostOS=X.HostOS;
|
||||
D->FileCRC=X.FileCRC;
|
||||
D->FileTime=X.FileTime;
|
||||
D->UnpVer=X.UnpVer;
|
||||
D->Method=X.Method;
|
||||
D->FileAttr=X.FileAttr;
|
||||
D->CmtSize=0;
|
||||
D->CmtState=0;
|
||||
|
||||
return Code;
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
try
|
||||
{
|
||||
if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0)
|
||||
{
|
||||
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC &&
|
||||
Data->Arc.EndArcHead.NextVolume)
|
||||
if (MergeArchive(Data->Arc,NULL,false,'L'))
|
||||
{
|
||||
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
|
||||
return RARReadHeaderEx(hArcData,D);
|
||||
}
|
||||
else
|
||||
return ERAR_EOPEN;
|
||||
|
||||
if (Data->Arc.BrokenHeader)
|
||||
return ERAR_BAD_DATA;
|
||||
|
||||
// Might be necessary if RARSetPassword is still called instead of
|
||||
// open callback for RAR5 archives and if password is invalid.
|
||||
if (Data->Arc.FailedHeaderDecryption)
|
||||
return ERAR_BAD_PASSWORD;
|
||||
|
||||
return ERAR_END_ARCHIVE;
|
||||
}
|
||||
FileHeader *hd=&Data->Arc.FileHead;
|
||||
if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore)
|
||||
{
|
||||
int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
|
||||
if (Code==0)
|
||||
return RARReadHeaderEx(hArcData,D);
|
||||
else
|
||||
return Code;
|
||||
}
|
||||
wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW));
|
||||
WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName));
|
||||
|
||||
wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW));
|
||||
WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName));
|
||||
#ifdef _WIN_ALL
|
||||
CharToOemA(D->FileName,D->FileName);
|
||||
#endif
|
||||
|
||||
D->Flags=0;
|
||||
if (hd->SplitBefore)
|
||||
D->Flags|=RHDF_SPLITBEFORE;
|
||||
if (hd->SplitAfter)
|
||||
D->Flags|=RHDF_SPLITAFTER;
|
||||
if (hd->Encrypted)
|
||||
D->Flags|=RHDF_ENCRYPTED;
|
||||
if (hd->Solid)
|
||||
D->Flags|=RHDF_SOLID;
|
||||
if (hd->Dir)
|
||||
D->Flags|=RHDF_DIRECTORY;
|
||||
|
||||
D->PackSize=uint(hd->PackSize & 0xffffffff);
|
||||
D->PackSizeHigh=uint(hd->PackSize>>32);
|
||||
D->UnpSize=uint(hd->UnpSize & 0xffffffff);
|
||||
D->UnpSizeHigh=uint(hd->UnpSize>>32);
|
||||
D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX;
|
||||
if (Data->Arc.Format==RARFMT50)
|
||||
D->UnpVer=Data->Arc.FileHead.UnpVer==0 ? 50 : 200; // If it is not 0, just set it to something big.
|
||||
else
|
||||
D->UnpVer=Data->Arc.FileHead.UnpVer;
|
||||
D->FileCRC=hd->FileHash.CRC32;
|
||||
D->FileTime=hd->mtime.GetDos();
|
||||
|
||||
uint64 MRaw=hd->mtime.GetWin();
|
||||
D->MtimeLow=(uint)MRaw;
|
||||
D->MtimeHigh=(uint)(MRaw>>32);
|
||||
uint64 CRaw=hd->ctime.GetWin();
|
||||
D->CtimeLow=(uint)CRaw;
|
||||
D->CtimeHigh=(uint)(CRaw>>32);
|
||||
uint64 ARaw=hd->atime.GetWin();
|
||||
D->AtimeLow=(uint)ARaw;
|
||||
D->AtimeHigh=(uint)(ARaw>>32);
|
||||
|
||||
D->Method=hd->Method+0x30;
|
||||
D->FileAttr=hd->FileAttr;
|
||||
D->CmtSize=0;
|
||||
D->CmtState=0;
|
||||
|
||||
D->DictSize=uint(hd->WinSize/1024);
|
||||
|
||||
switch (hd->FileHash.Type)
|
||||
{
|
||||
case HASH_RAR14:
|
||||
case HASH_CRC32:
|
||||
D->HashType=RAR_HASH_CRC32;
|
||||
break;
|
||||
case HASH_BLAKE2:
|
||||
D->HashType=RAR_HASH_BLAKE2;
|
||||
memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
|
||||
break;
|
||||
default:
|
||||
D->HashType=RAR_HASH_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
D->RedirType=hd->RedirType;
|
||||
// RedirNameSize sanity check is useful in case some developer
|
||||
// did not initialize Reserved area with 0 as required in docs.
|
||||
// We have taken 'Redir*' fields from Reserved area. We may remove
|
||||
// this RedirNameSize check sometimes later.
|
||||
if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL &&
|
||||
D->RedirNameSize>0 && D->RedirNameSize<100000)
|
||||
wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize);
|
||||
D->DirTarget=hd->DirTarget;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
|
||||
}
|
||||
return ERAR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
try
|
||||
{
|
||||
Data->Cmd.DllError=0;
|
||||
if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
|
||||
Operation==RAR_SKIP && !Data->Arc.Solid)
|
||||
{
|
||||
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
|
||||
Data->Arc.FileHead.SplitAfter)
|
||||
if (MergeArchive(Data->Arc,NULL,false,'L'))
|
||||
{
|
||||
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
|
||||
return ERAR_SUCCESS;
|
||||
}
|
||||
else
|
||||
return ERAR_EOPEN;
|
||||
Data->Arc.SeekToNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
Data->Cmd.DllOpMode=Operation;
|
||||
|
||||
*Data->Cmd.ExtrPath=0;
|
||||
*Data->Cmd.DllDestName=0;
|
||||
|
||||
if (DestPath!=NULL)
|
||||
{
|
||||
char ExtrPathA[NM];
|
||||
strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2);
|
||||
#ifdef _WIN_ALL
|
||||
// We must not apply OemToCharBuffA directly to DestPath,
|
||||
// because we do not know DestPath length and OemToCharBuffA
|
||||
// does not stop at 0.
|
||||
OemToCharA(ExtrPathA,ExtrPathA);
|
||||
#endif
|
||||
CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
|
||||
AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
|
||||
}
|
||||
if (DestName!=NULL)
|
||||
{
|
||||
char DestNameA[NM];
|
||||
strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2);
|
||||
#ifdef _WIN_ALL
|
||||
// We must not apply OemToCharBuffA directly to DestName,
|
||||
// because we do not know DestName length and OemToCharBuffA
|
||||
// does not stop at 0.
|
||||
OemToCharA(DestNameA,DestNameA);
|
||||
#endif
|
||||
CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName));
|
||||
}
|
||||
|
||||
if (DestPathW!=NULL)
|
||||
{
|
||||
wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath));
|
||||
AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
|
||||
}
|
||||
|
||||
if (DestNameW!=NULL)
|
||||
wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
|
||||
|
||||
wcscpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T");
|
||||
Data->Cmd.Test=Operation!=RAR_EXTRACT;
|
||||
bool Repeat=false;
|
||||
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
|
||||
|
||||
// Now we process extra file information if any.
|
||||
//
|
||||
// Archive can be closed if we process volumes, next volume is missing
|
||||
// and current one is already removed or deleted. So we need to check
|
||||
// if archive is still open to avoid calling file operations on
|
||||
// the invalid file handle. Some of our file operations like Seek()
|
||||
// process such invalid handle correctly, some not.
|
||||
while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 &&
|
||||
Data->Arc.GetHeaderType()==HEAD_SERVICE)
|
||||
{
|
||||
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
|
||||
Data->Arc.SeekToNext();
|
||||
}
|
||||
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
|
||||
}
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
return ERAR_NO_MEMORY;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
|
||||
}
|
||||
return Data->Cmd.DllError;
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName)
|
||||
{
|
||||
return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL));
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName)
|
||||
{
|
||||
return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName));
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
Data->Cmd.ChangeVolProc=ChangeVolProc;
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
Data->Cmd.Callback=Callback;
|
||||
Data->Cmd.UserData=UserData;
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
Data->Cmd.ProcessDataProc=ProcessDataProc;
|
||||
}
|
||||
|
||||
|
||||
#ifndef RAR_NOCRYPT
|
||||
void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
wchar PasswordW[MAXPASSWORD];
|
||||
GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW));
|
||||
Data->Cmd.Password.Set(PasswordW);
|
||||
cleandata(PasswordW,sizeof(PasswordW));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int PASCAL RARGetDllVersion()
|
||||
{
|
||||
return RAR_DLL_VERSION;
|
||||
}
|
||||
|
||||
|
||||
static int RarErrorToDll(RAR_EXIT ErrCode)
|
||||
{
|
||||
switch(ErrCode)
|
||||
{
|
||||
case RARX_FATAL:
|
||||
return ERAR_EREAD;
|
||||
case RARX_CRC:
|
||||
return ERAR_BAD_DATA;
|
||||
case RARX_WRITE:
|
||||
return ERAR_EWRITE;
|
||||
case RARX_OPEN:
|
||||
return ERAR_EOPEN;
|
||||
case RARX_CREATE:
|
||||
return ERAR_ECREATE;
|
||||
case RARX_MEMORY:
|
||||
return ERAR_NO_MEMORY;
|
||||
case RARX_BADPWD:
|
||||
return ERAR_BAD_PASSWORD;
|
||||
case RARX_SUCCESS:
|
||||
return ERAR_SUCCESS; // 0.
|
||||
default:
|
||||
return ERAR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
EXPORTS
|
||||
RAROpenArchive
|
||||
RAROpenArchiveEx
|
||||
RARCloseArchive
|
||||
RARReadHeader
|
||||
RARReadHeaderEx
|
||||
RARProcessFile
|
||||
RARSetCallback
|
||||
RARSetChangeVolProc
|
||||
RARSetProcessDataProc
|
||||
RARSetPassword
|
||||
RARGetDllVersion
|
||||
185
unrar/dll.hpp
185
unrar/dll.hpp
@@ -1,185 +0,0 @@
|
||||
#ifndef _UNRAR_DLL_
|
||||
#define _UNRAR_DLL_
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
#define ERAR_SUCCESS 0
|
||||
#define ERAR_END_ARCHIVE 10
|
||||
#define ERAR_NO_MEMORY 11
|
||||
#define ERAR_BAD_DATA 12
|
||||
#define ERAR_BAD_ARCHIVE 13
|
||||
#define ERAR_UNKNOWN_FORMAT 14
|
||||
#define ERAR_EOPEN 15
|
||||
#define ERAR_ECREATE 16
|
||||
#define ERAR_ECLOSE 17
|
||||
#define ERAR_EREAD 18
|
||||
#define ERAR_EWRITE 19
|
||||
#define ERAR_SMALL_BUF 20
|
||||
#define ERAR_UNKNOWN 21
|
||||
#define ERAR_MISSING_PASSWORD 22
|
||||
#define ERAR_EREFERENCE 23
|
||||
#define ERAR_BAD_PASSWORD 24
|
||||
|
||||
#define RAR_OM_LIST 0
|
||||
#define RAR_OM_EXTRACT 1
|
||||
#define RAR_OM_LIST_INCSPLIT 2
|
||||
|
||||
#define RAR_SKIP 0
|
||||
#define RAR_TEST 1
|
||||
#define RAR_EXTRACT 2
|
||||
|
||||
#define RAR_VOL_ASK 0
|
||||
#define RAR_VOL_NOTIFY 1
|
||||
|
||||
#define RAR_DLL_VERSION 8
|
||||
|
||||
#define RAR_HASH_NONE 0
|
||||
#define RAR_HASH_CRC32 1
|
||||
#define RAR_HASH_BLAKE2 2
|
||||
|
||||
|
||||
#ifdef _UNIX
|
||||
#define CALLBACK
|
||||
#define PASCAL
|
||||
#define LONG long
|
||||
#define HANDLE void *
|
||||
#define LPARAM long
|
||||
#define UINT unsigned int
|
||||
#endif
|
||||
|
||||
#define RHDF_SPLITBEFORE 0x01
|
||||
#define RHDF_SPLITAFTER 0x02
|
||||
#define RHDF_ENCRYPTED 0x04
|
||||
#define RHDF_SOLID 0x10
|
||||
#define RHDF_DIRECTORY 0x20
|
||||
|
||||
|
||||
struct RARHeaderData
|
||||
{
|
||||
char ArcName[260];
|
||||
char FileName[260];
|
||||
unsigned int Flags;
|
||||
unsigned int PackSize;
|
||||
unsigned int UnpSize;
|
||||
unsigned int HostOS;
|
||||
unsigned int FileCRC;
|
||||
unsigned int FileTime;
|
||||
unsigned int UnpVer;
|
||||
unsigned int Method;
|
||||
unsigned int FileAttr;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
};
|
||||
|
||||
|
||||
struct RARHeaderDataEx
|
||||
{
|
||||
char ArcName[1024];
|
||||
wchar_t ArcNameW[1024];
|
||||
char FileName[1024];
|
||||
wchar_t FileNameW[1024];
|
||||
unsigned int Flags;
|
||||
unsigned int PackSize;
|
||||
unsigned int PackSizeHigh;
|
||||
unsigned int UnpSize;
|
||||
unsigned int UnpSizeHigh;
|
||||
unsigned int HostOS;
|
||||
unsigned int FileCRC;
|
||||
unsigned int FileTime;
|
||||
unsigned int UnpVer;
|
||||
unsigned int Method;
|
||||
unsigned int FileAttr;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
unsigned int DictSize;
|
||||
unsigned int HashType;
|
||||
char Hash[32];
|
||||
unsigned int RedirType;
|
||||
wchar_t *RedirName;
|
||||
unsigned int RedirNameSize;
|
||||
unsigned int DirTarget;
|
||||
unsigned int MtimeLow;
|
||||
unsigned int MtimeHigh;
|
||||
unsigned int CtimeLow;
|
||||
unsigned int CtimeHigh;
|
||||
unsigned int AtimeLow;
|
||||
unsigned int AtimeHigh;
|
||||
unsigned int Reserved[988];
|
||||
};
|
||||
|
||||
|
||||
struct RAROpenArchiveData
|
||||
{
|
||||
char *ArcName;
|
||||
unsigned int OpenMode;
|
||||
unsigned int OpenResult;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
};
|
||||
|
||||
typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
|
||||
|
||||
#define ROADF_VOLUME 0x0001
|
||||
#define ROADF_COMMENT 0x0002
|
||||
#define ROADF_LOCK 0x0004
|
||||
#define ROADF_SOLID 0x0008
|
||||
#define ROADF_NEWNUMBERING 0x0010
|
||||
#define ROADF_SIGNED 0x0020
|
||||
#define ROADF_RECOVERY 0x0040
|
||||
#define ROADF_ENCHEADERS 0x0080
|
||||
#define ROADF_FIRSTVOLUME 0x0100
|
||||
|
||||
struct RAROpenArchiveDataEx
|
||||
{
|
||||
char *ArcName;
|
||||
wchar_t *ArcNameW;
|
||||
unsigned int OpenMode;
|
||||
unsigned int OpenResult;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
unsigned int Flags;
|
||||
UNRARCALLBACK Callback;
|
||||
LPARAM UserData;
|
||||
unsigned int Reserved[28];
|
||||
};
|
||||
|
||||
enum UNRARCALLBACK_MESSAGES {
|
||||
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
|
||||
UCM_NEEDPASSWORDW
|
||||
};
|
||||
|
||||
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
|
||||
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData);
|
||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData);
|
||||
int PASCAL RARCloseArchive(HANDLE hArcData);
|
||||
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData);
|
||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData);
|
||||
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName);
|
||||
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName);
|
||||
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData);
|
||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc);
|
||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc);
|
||||
void PASCAL RARSetPassword(HANDLE hArcData,char *Password);
|
||||
int PASCAL RARGetDllVersion();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
28
unrar/dll.rc
28
unrar/dll.rc
@@ -1,28 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 5, 50, 100, 2418
|
||||
PRODUCTVERSION 5, 50, 100, 2418
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
{
|
||||
BLOCK "StringFileInfo"
|
||||
{
|
||||
BLOCK "040904E4"
|
||||
{
|
||||
VALUE "CompanyName", "Alexander Roshal\0"
|
||||
VALUE "ProductName", "RAR decompression library\0"
|
||||
VALUE "FileDescription", "RAR decompression library\0"
|
||||
VALUE "FileVersion", "5.50.0\0"
|
||||
VALUE "ProductVersion", "5.50.0\0"
|
||||
VALUE "LegalCopyright", "Copyright <20> Alexander Roshal 1993-2017\0"
|
||||
VALUE "OriginalFilename", "Unrar.dll\0"
|
||||
}
|
||||
}
|
||||
BLOCK "VarFileInfo"
|
||||
{
|
||||
VALUE "Translation", 0x0409, 0x04E4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
EncodeFileName::EncodeFileName()
|
||||
{
|
||||
Flags=0;
|
||||
FlagBits=0;
|
||||
FlagsPos=0;
|
||||
DestSize=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW,
|
||||
size_t MaxDecSize)
|
||||
{
|
||||
size_t EncPos=0,DecPos=0;
|
||||
byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0;
|
||||
while (EncPos<EncSize && DecPos<MaxDecSize)
|
||||
{
|
||||
if (FlagBits==0)
|
||||
{
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
Flags=EncName[EncPos++];
|
||||
FlagBits=8;
|
||||
}
|
||||
switch(Flags>>6)
|
||||
{
|
||||
case 0:
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
NameW[DecPos++]=EncName[EncPos++];
|
||||
break;
|
||||
case 1:
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
|
||||
break;
|
||||
case 2:
|
||||
if (EncPos+1>=EncSize)
|
||||
break;
|
||||
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
|
||||
EncPos+=2;
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
int Length=EncName[EncPos++];
|
||||
if ((Length & 0x80)!=0)
|
||||
{
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
byte Correction=EncName[EncPos++];
|
||||
for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++)
|
||||
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
|
||||
}
|
||||
else
|
||||
for (Length+=2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++)
|
||||
NameW[DecPos]=Name[DecPos];
|
||||
}
|
||||
break;
|
||||
}
|
||||
Flags<<=2;
|
||||
FlagBits-=2;
|
||||
}
|
||||
NameW[DecPos<MaxDecSize ? DecPos:MaxDecSize-1]=0;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef _RAR_ENCNAME_
|
||||
#define _RAR_ENCNAME_
|
||||
|
||||
class EncodeFileName
|
||||
{
|
||||
private:
|
||||
void AddFlags(int Value);
|
||||
|
||||
byte *EncName;
|
||||
byte Flags;
|
||||
uint FlagBits;
|
||||
size_t FlagsPos;
|
||||
size_t DestSize;
|
||||
public:
|
||||
EncodeFileName();
|
||||
size_t Encode(char *Name,wchar *NameW,byte *EncName);
|
||||
void Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
391
unrar/errhnd.cpp
391
unrar/errhnd.cpp
@@ -1,391 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
ErrorHandler::ErrorHandler()
|
||||
{
|
||||
Clean();
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::Clean()
|
||||
{
|
||||
ExitCode=RARX_SUCCESS;
|
||||
ErrCount=0;
|
||||
EnableBreak=true;
|
||||
Silent=false;
|
||||
UserBreak=false;
|
||||
MainExit=false;
|
||||
DisableShutdown=false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::MemoryError()
|
||||
{
|
||||
MemoryErrorMsg();
|
||||
Exit(RARX_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::OpenError(const wchar *FileName)
|
||||
{
|
||||
#ifndef SILENT
|
||||
OpenErrorMsg(FileName);
|
||||
Exit(RARX_OPEN);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::CloseError(const wchar *FileName)
|
||||
{
|
||||
if (!UserBreak)
|
||||
{
|
||||
uiMsg(UIERROR_FILECLOSE,FileName);
|
||||
SysErrMsg();
|
||||
}
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_FATAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ReadError(const wchar *FileName)
|
||||
{
|
||||
#ifndef SILENT
|
||||
ReadErrorMsg(FileName);
|
||||
#endif
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_FATAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool ErrorHandler::AskRepeatRead(const wchar *FileName)
|
||||
{
|
||||
#if !defined(SILENT) && !defined(SFX_MODULE)
|
||||
if (!Silent)
|
||||
{
|
||||
SysErrMsg();
|
||||
bool Repeat=uiAskRepeatRead(FileName);
|
||||
if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog.
|
||||
DisableShutdown=true;
|
||||
return Repeat;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
#ifndef SILENT
|
||||
WriteErrorMsg(ArcName,FileName);
|
||||
#endif
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
void ErrorHandler::WriteErrorFAT(const wchar *FileName)
|
||||
{
|
||||
SysErrMsg();
|
||||
uiMsg(UIERROR_NTFSREQUIRED,FileName);
|
||||
#if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL)
|
||||
Exit(RARX_WRITE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull)
|
||||
{
|
||||
#ifndef SILENT
|
||||
if (!Silent)
|
||||
{
|
||||
// We do not display "repeat write" prompt in Android, so we do not
|
||||
// need the matching system error message.
|
||||
SysErrMsg();
|
||||
bool Repeat=uiAskRepeatWrite(FileName,DiskFull);
|
||||
if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog.
|
||||
DisableShutdown=true;
|
||||
return Repeat;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SeekError(const wchar *FileName)
|
||||
{
|
||||
if (!UserBreak)
|
||||
{
|
||||
uiMsg(UIERROR_FILESEEK,FileName);
|
||||
SysErrMsg();
|
||||
}
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_FATAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::GeneralErrMsg(const wchar *fmt,...)
|
||||
{
|
||||
va_list arglist;
|
||||
va_start(arglist,fmt);
|
||||
wchar Msg[1024];
|
||||
vswprintf(Msg,ASIZE(Msg),fmt,arglist);
|
||||
uiMsg(UIERROR_GENERALERRMSG,Msg);
|
||||
SysErrMsg();
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::MemoryErrorMsg()
|
||||
{
|
||||
uiMsg(UIERROR_MEMORY);
|
||||
SetErrorCode(RARX_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::OpenErrorMsg(const wchar *FileName)
|
||||
{
|
||||
OpenErrorMsg(NULL,FileName);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILEOPEN,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_OPEN);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::CreateErrorMsg(const wchar *FileName)
|
||||
{
|
||||
CreateErrorMsg(NULL,FileName);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILECREATE,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_CREATE);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ReadErrorMsg(const wchar *FileName)
|
||||
{
|
||||
ReadErrorMsg(NULL,FileName);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILEREAD,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_FATAL);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_WRITE);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ArcBrokenMsg(const wchar *ArcName)
|
||||
{
|
||||
uiMsg(UIERROR_ARCBROKEN,ArcName);
|
||||
SetErrorCode(RARX_CRC);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
|
||||
SetErrorCode(RARX_CRC);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName)
|
||||
{
|
||||
uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
|
||||
ErrHandler.SetErrorCode(RARX_FATAL);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::Exit(RAR_EXIT ExitCode)
|
||||
{
|
||||
uiAlarm(UIALARM_ERROR);
|
||||
Throw(ExitCode);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SetErrorCode(RAR_EXIT Code)
|
||||
{
|
||||
switch(Code)
|
||||
{
|
||||
case RARX_WARNING:
|
||||
case RARX_USERBREAK:
|
||||
if (ExitCode==RARX_SUCCESS)
|
||||
ExitCode=Code;
|
||||
break;
|
||||
case RARX_CRC:
|
||||
if (ExitCode!=RARX_BADPWD)
|
||||
ExitCode=Code;
|
||||
break;
|
||||
case RARX_FATAL:
|
||||
if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING)
|
||||
ExitCode=RARX_FATAL;
|
||||
break;
|
||||
default:
|
||||
ExitCode=Code;
|
||||
break;
|
||||
}
|
||||
ErrCount++;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
BOOL __stdcall ProcessSignal(DWORD SigType)
|
||||
#else
|
||||
#if defined(__sun)
|
||||
extern "C"
|
||||
#endif
|
||||
void _stdfunction ProcessSignal(int SigType)
|
||||
#endif
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// When a console application is run as a service, this allows the service
|
||||
// to continue running after the user logs off.
|
||||
if (SigType==CTRL_LOGOFF_EVENT)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
ErrHandler.UserBreak=true;
|
||||
mprintf(St(MBreak));
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
// Let the main thread to handle 'throw' and destroy file objects.
|
||||
for (uint I=0;!ErrHandler.MainExit && I<50;I++)
|
||||
Sleep(100);
|
||||
#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL)
|
||||
ExtRes.UnloadDLL();
|
||||
#endif
|
||||
exit(RARX_USERBREAK);
|
||||
#endif
|
||||
|
||||
#ifdef _UNIX
|
||||
static uint BreakCount=0;
|
||||
// User continues to press Ctrl+C, exit immediately without cleanup.
|
||||
if (++BreakCount>1)
|
||||
exit(RARX_USERBREAK);
|
||||
// Otherwise return from signal handler and let Wait() function to close
|
||||
// files and quit. We cannot use the same approach as in Windows,
|
||||
// because Unix signal handler can block execution of our main code.
|
||||
#endif
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(_MSC_VER)
|
||||
// never reached, just to avoid a compiler warning
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SetSignalHandlers(bool Enable)
|
||||
{
|
||||
EnableBreak=Enable;
|
||||
#ifdef _WIN_ALL
|
||||
SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE);
|
||||
#else
|
||||
signal(SIGINT,Enable ? ProcessSignal:SIG_IGN);
|
||||
signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::Throw(RAR_EXIT Code)
|
||||
{
|
||||
if (Code==RARX_USERBREAK && !EnableBreak)
|
||||
return;
|
||||
#if !defined(SILENT)
|
||||
// Do not write "aborted" when just displaying online help.
|
||||
if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR)
|
||||
mprintf(L"\n%s\n",St(MProgAborted));
|
||||
#endif
|
||||
SetErrorCode(Code);
|
||||
throw Code;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SysErrMsg()
|
||||
{
|
||||
#if !defined(SFX_MODULE) && !defined(SILENT)
|
||||
#ifdef _WIN_ALL
|
||||
wchar *lpMsgBuf=NULL;
|
||||
int ErrType=GetLastError();
|
||||
if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf,0,NULL))
|
||||
{
|
||||
wchar *CurMsg=lpMsgBuf;
|
||||
while (CurMsg!=NULL)
|
||||
{
|
||||
while (*CurMsg=='\r' || *CurMsg=='\n')
|
||||
CurMsg++;
|
||||
if (*CurMsg==0)
|
||||
break;
|
||||
wchar *EndMsg=wcschr(CurMsg,'\r');
|
||||
if (EndMsg==NULL)
|
||||
EndMsg=wcschr(CurMsg,'\n');
|
||||
if (EndMsg!=NULL)
|
||||
{
|
||||
*EndMsg=0;
|
||||
EndMsg++;
|
||||
}
|
||||
uiMsg(UIERROR_SYSERRMSG,CurMsg);
|
||||
CurMsg=EndMsg;
|
||||
}
|
||||
}
|
||||
LocalFree( lpMsgBuf );
|
||||
#endif
|
||||
|
||||
#if defined(_UNIX) || defined(_EMX)
|
||||
if (errno!=0)
|
||||
{
|
||||
char *err=strerror(errno);
|
||||
if (err!=NULL)
|
||||
{
|
||||
wchar Msg[1024];
|
||||
CharToWide(err,Msg,ASIZE(Msg));
|
||||
uiMsg(UIERROR_SYSERRMSG,Msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int ErrorHandler::GetSystemErrorCode()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return GetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SetSystemErrorCode(int Code)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
SetLastError(Code);
|
||||
#else
|
||||
errno=Code;
|
||||
#endif
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
#ifndef _RAR_ERRHANDLER_
|
||||
#define _RAR_ERRHANDLER_
|
||||
|
||||
enum RAR_EXIT // RAR exit code.
|
||||
{
|
||||
RARX_SUCCESS = 0,
|
||||
RARX_WARNING = 1,
|
||||
RARX_FATAL = 2,
|
||||
RARX_CRC = 3,
|
||||
RARX_LOCK = 4,
|
||||
RARX_WRITE = 5,
|
||||
RARX_OPEN = 6,
|
||||
RARX_USERERROR = 7,
|
||||
RARX_MEMORY = 8,
|
||||
RARX_CREATE = 9,
|
||||
RARX_NOFILES = 10,
|
||||
RARX_BADPWD = 11,
|
||||
RARX_USERBREAK = 255
|
||||
};
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
private:
|
||||
RAR_EXIT ExitCode;
|
||||
uint ErrCount;
|
||||
bool EnableBreak;
|
||||
bool Silent;
|
||||
bool DisableShutdown; // Shutdown is not suitable after last error.
|
||||
public:
|
||||
ErrorHandler();
|
||||
void Clean();
|
||||
void MemoryError();
|
||||
void OpenError(const wchar *FileName);
|
||||
void CloseError(const wchar *FileName);
|
||||
void ReadError(const wchar *FileName);
|
||||
bool AskRepeatRead(const wchar *FileName);
|
||||
void WriteError(const wchar *ArcName,const wchar *FileName);
|
||||
void WriteErrorFAT(const wchar *FileName);
|
||||
bool AskRepeatWrite(const wchar *FileName,bool DiskFull);
|
||||
void SeekError(const wchar *FileName);
|
||||
void GeneralErrMsg(const wchar *fmt,...);
|
||||
void MemoryErrorMsg();
|
||||
void OpenErrorMsg(const wchar *FileName);
|
||||
void OpenErrorMsg(const wchar *ArcName,const wchar *FileName);
|
||||
void CreateErrorMsg(const wchar *FileName);
|
||||
void CreateErrorMsg(const wchar *ArcName,const wchar *FileName);
|
||||
void ReadErrorMsg(const wchar *FileName);
|
||||
void ReadErrorMsg(const wchar *ArcName,const wchar *FileName);
|
||||
void WriteErrorMsg(const wchar *ArcName,const wchar *FileName);
|
||||
void ArcBrokenMsg(const wchar *ArcName);
|
||||
void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName);
|
||||
void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName);
|
||||
void Exit(RAR_EXIT ExitCode);
|
||||
void SetErrorCode(RAR_EXIT Code);
|
||||
RAR_EXIT GetErrorCode() {return ExitCode;}
|
||||
uint GetErrorCount() {return ErrCount;}
|
||||
void SetSignalHandlers(bool Enable);
|
||||
void Throw(RAR_EXIT Code);
|
||||
void SetSilent(bool Mode) {Silent=Mode;};
|
||||
void SysErrMsg();
|
||||
int GetSystemErrorCode();
|
||||
void SetSystemErrorCode(int Code);
|
||||
bool IsShutdownEnabled() {return !DisableShutdown;}
|
||||
|
||||
bool UserBreak; // Ctrl+Break is pressed.
|
||||
bool MainExit; // main() is completed.
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,178 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "hardlinks.cpp"
|
||||
#include "win32stm.cpp"
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
#include "win32acl.cpp"
|
||||
#include "win32lnk.cpp"
|
||||
#endif
|
||||
|
||||
#ifdef _UNIX
|
||||
#include "uowners.cpp"
|
||||
#ifdef SAVE_LINKS
|
||||
#include "ulinks.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// RAR2 service header extra records.
|
||||
#ifndef SFX_MODULE
|
||||
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
|
||||
{
|
||||
if (Cmd->Test)
|
||||
return;
|
||||
switch(Arc.SubBlockHead.SubType)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
case UO_HEAD:
|
||||
if (Cmd->ProcessOwners)
|
||||
ExtractUnixOwner20(Arc,Name);
|
||||
break;
|
||||
#endif
|
||||
#ifdef _WIN_ALL
|
||||
case NTACL_HEAD:
|
||||
if (Cmd->ProcessOwners)
|
||||
ExtractACL20(Arc,Name);
|
||||
break;
|
||||
case STREAM_HEAD:
|
||||
ExtractStreams20(Arc,Name);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// RAR3 and RAR5 service header extra records.
|
||||
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
|
||||
Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
|
||||
ExtractUnixOwner30(Arc,Name);
|
||||
#endif
|
||||
#ifdef _WIN_ALL
|
||||
if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
|
||||
ExtractACL(Arc,Name);
|
||||
if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
|
||||
ExtractStreams(Arc,Name,Cmd->Test);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Extra data stored directly in file header.
|
||||
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
|
||||
SetUnixOwner(Arc,Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Calculate a number of path components except \. and \..
|
||||
static int CalcAllowedDepth(const wchar *Name)
|
||||
{
|
||||
int AllowedDepth=0;
|
||||
while (*Name!=0)
|
||||
{
|
||||
if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
|
||||
{
|
||||
bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
|
||||
bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
|
||||
if (!Dot && !Dot2)
|
||||
AllowedDepth++;
|
||||
}
|
||||
Name++;
|
||||
}
|
||||
return AllowedDepth;
|
||||
}
|
||||
|
||||
|
||||
// Check if all existing path components are directories and not links.
|
||||
static bool LinkInPath(const wchar *Name)
|
||||
{
|
||||
wchar Path[NM];
|
||||
if (wcslen(Name)>=ASIZE(Path))
|
||||
return true; // It should not be that long, skip.
|
||||
wcsncpyz(Path,Name,ASIZE(Path));
|
||||
for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
|
||||
if (IsPathDiv(*s))
|
||||
{
|
||||
*s=0;
|
||||
FindData FD;
|
||||
if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
|
||||
{
|
||||
// Catch root dir based /path/file paths also as stuff like \\?\.
|
||||
// Do not check PrepSrcName here, it can be root based if destination path
|
||||
// is a root based.
|
||||
if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
|
||||
return false;
|
||||
|
||||
// Number of ".." in link target.
|
||||
int UpLevels=0;
|
||||
for (int Pos=0;*TargetName!=0;Pos++)
|
||||
{
|
||||
bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
|
||||
(IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
|
||||
(Pos==0 || IsPathDiv(*(TargetName-1)));
|
||||
if (Dot2)
|
||||
UpLevels++;
|
||||
TargetName++;
|
||||
}
|
||||
// If link target includes "..", it must not have another links
|
||||
// in the path, because they can bypass our safety check. For example,
|
||||
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
|
||||
// or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
|
||||
if (UpLevels>0 && LinkInPath(PrepSrcName))
|
||||
return false;
|
||||
|
||||
// We could check just prepared src name, but for extra safety
|
||||
// we check both original (as from archive header) and prepared
|
||||
// (after applying the destination path and -ep switches) names.
|
||||
|
||||
int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
|
||||
|
||||
// Remove the destination path from prepared name if any. We should not
|
||||
// count the destination path depth, because the link target must point
|
||||
// inside of this path, not outside of it.
|
||||
size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
|
||||
if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
|
||||
{
|
||||
PrepSrcName+=ExtrPathLength;
|
||||
while (IsPathDiv(*PrepSrcName))
|
||||
PrepSrcName++;
|
||||
}
|
||||
int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
|
||||
|
||||
return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
|
||||
}
|
||||
|
||||
|
||||
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
|
||||
{
|
||||
#if defined(SAVE_LINKS) && defined(_UNIX)
|
||||
// For RAR 3.x archives we process links even in test mode to skip link data.
|
||||
if (Arc.Format==RARFMT15)
|
||||
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
|
||||
if (Arc.Format==RARFMT50)
|
||||
return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
|
||||
#elif defined _WIN_ALL
|
||||
// RAR 5.0 archives store link information in file header, so there is
|
||||
// no need to additionally test it if we do not create a file.
|
||||
if (Arc.Format==RARFMT50)
|
||||
return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#ifndef _RAR_EXTINFO_
|
||||
#define _RAR_EXTINFO_
|
||||
|
||||
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
|
||||
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
|
||||
#ifdef _UNIX
|
||||
void SetUnixOwner(Archive &Arc,const wchar *FileName);
|
||||
#endif
|
||||
|
||||
bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
|
||||
|
||||
void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize);
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
bool SetPrivilege(LPCTSTR PrivName);
|
||||
#endif
|
||||
|
||||
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name);
|
||||
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name);
|
||||
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name);
|
||||
|
||||
|
||||
#endif
|
||||
1167
unrar/extract.cpp
1167
unrar/extract.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
#ifndef _RAR_EXTRACT_
|
||||
#define _RAR_EXTRACT_
|
||||
|
||||
enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
|
||||
|
||||
class CmdExtract
|
||||
{
|
||||
private:
|
||||
EXTRACT_ARC_CODE ExtractArchive();
|
||||
bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
|
||||
void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
|
||||
#ifdef RARDLL
|
||||
bool ExtrDllGetPassword();
|
||||
#else
|
||||
bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName);
|
||||
#endif
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
|
||||
#endif
|
||||
void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName);
|
||||
bool ExtrCreateFile(Archive &Arc,File &CurFile);
|
||||
bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
|
||||
|
||||
RarTime StartTime; // time when extraction started
|
||||
|
||||
CommandData *Cmd;
|
||||
|
||||
ComprDataIO DataIO;
|
||||
Unpack *Unp;
|
||||
unsigned long TotalFileCount;
|
||||
|
||||
unsigned long FileCount;
|
||||
unsigned long MatchedArgs;
|
||||
bool FirstFile;
|
||||
bool AllMatchesExact;
|
||||
bool ReconstructDone;
|
||||
|
||||
// If any non-zero solid file was successfully unpacked before current.
|
||||
// If true and if current encrypted file is broken, obviously
|
||||
// the password is correct and we can report broken CRC without
|
||||
// any wrong password hints.
|
||||
bool AnySolidDataUnpackedWell;
|
||||
|
||||
wchar ArcName[NM];
|
||||
|
||||
bool PasswordAll;
|
||||
bool PrevProcessed; // If previous file was successfully extracted or tested.
|
||||
wchar DestFileName[NM];
|
||||
bool PasswordCancelled;
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
|
||||
bool Fat32,NotFat32;
|
||||
#endif
|
||||
public:
|
||||
CmdExtract(CommandData *Cmd);
|
||||
~CmdExtract();
|
||||
void DoExtract();
|
||||
void ExtractArchiveInit(Archive &Arc);
|
||||
bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
|
||||
static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,163 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
// If NewFile==NULL, we delete created file after user confirmation.
|
||||
// It is useful we we need to overwrite an existing folder or file,
|
||||
// but need user confirmation for that.
|
||||
bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
|
||||
bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
|
||||
{
|
||||
if (UserReject!=NULL)
|
||||
*UserReject=false;
|
||||
#ifdef _WIN_ALL
|
||||
bool ShortNameChanged=false;
|
||||
#endif
|
||||
while (FileExist(Name))
|
||||
{
|
||||
#if defined(_WIN_ALL)
|
||||
if (!ShortNameChanged)
|
||||
{
|
||||
// Avoid the infinite loop if UpdateExistingShortName returns
|
||||
// the same name.
|
||||
ShortNameChanged=true;
|
||||
|
||||
// Maybe our long name matches the short name of existing file.
|
||||
// Let's check if we can change the short name.
|
||||
if (UpdateExistingShortName(Name))
|
||||
continue;
|
||||
}
|
||||
// Allow short name check again. It is necessary, because rename and
|
||||
// autorename below can change the name, so we need to check it again.
|
||||
ShortNameChanged=false;
|
||||
#endif
|
||||
UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
|
||||
|
||||
if (Choice==UIASKREP_R_REPLACE)
|
||||
break;
|
||||
if (Choice==UIASKREP_R_SKIP)
|
||||
{
|
||||
if (UserReject!=NULL)
|
||||
*UserReject=true;
|
||||
return false;
|
||||
}
|
||||
if (Choice==UIASKREP_R_CANCEL)
|
||||
ErrHandler.Exit(RARX_USERBREAK);
|
||||
}
|
||||
|
||||
// Try to truncate the existing file first instead of delete,
|
||||
// so we preserve existing file permissions such as NTFS permissions.
|
||||
uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
|
||||
if (NewFile!=NULL && NewFile->Create(Name,FileMode))
|
||||
return true;
|
||||
|
||||
CreatePath(Name,true);
|
||||
return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
|
||||
}
|
||||
|
||||
|
||||
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
|
||||
{
|
||||
wchar NewName[NM];
|
||||
size_t NameLength=wcslen(Name);
|
||||
wchar *Ext=GetExt(Name);
|
||||
if (Ext==NULL)
|
||||
Ext=Name+NameLength;
|
||||
for (uint FileVer=1;;FileVer++)
|
||||
{
|
||||
swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
|
||||
if (!FileExist(NewName))
|
||||
{
|
||||
wcsncpyz(Name,NewName,MaxNameSize);
|
||||
break;
|
||||
}
|
||||
if (FileVer>=1000000)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN_ALL)
|
||||
// If we find a file, which short name is equal to 'Name', we try to change
|
||||
// its short name, while preserving the long name. It helps when unpacking
|
||||
// an archived file, which long name is equal to short name of already
|
||||
// existing file. Otherwise we would overwrite the already existing file,
|
||||
// even though its long name does not match the name of unpacking file.
|
||||
bool UpdateExistingShortName(const wchar *Name)
|
||||
{
|
||||
wchar LongPathName[NM];
|
||||
DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
|
||||
if (Res==0 || Res>=ASIZE(LongPathName))
|
||||
return false;
|
||||
wchar ShortPathName[NM];
|
||||
Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
|
||||
if (Res==0 || Res>=ASIZE(ShortPathName))
|
||||
return false;
|
||||
wchar *LongName=PointToName(LongPathName);
|
||||
wchar *ShortName=PointToName(ShortPathName);
|
||||
|
||||
// We continue only if file has a short name, which does not match its
|
||||
// long name, and this short name is equal to name of file which we need
|
||||
// to create.
|
||||
if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
|
||||
wcsicomp(PointToName(Name),ShortName)!=0)
|
||||
return false;
|
||||
|
||||
// Generate the temporary new name for existing file.
|
||||
wchar NewName[NM];
|
||||
*NewName=0;
|
||||
for (int I=0;I<10000 && *NewName==0;I+=123)
|
||||
{
|
||||
// Here we copy the path part of file to create. We'll make the temporary
|
||||
// file in the same folder.
|
||||
wcsncpyz(NewName,Name,ASIZE(NewName));
|
||||
|
||||
// Here we set the random name part.
|
||||
swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
|
||||
|
||||
// If such file is already exist, try next random name.
|
||||
if (FileExist(NewName))
|
||||
*NewName=0;
|
||||
}
|
||||
|
||||
// If we could not generate the name not used by any other file, we return.
|
||||
if (*NewName==0)
|
||||
return false;
|
||||
|
||||
// FastFind returns the name without path, but we need the fully qualified
|
||||
// name for renaming, so we use the path from file to create and long name
|
||||
// from existing file.
|
||||
wchar FullName[NM];
|
||||
wcsncpyz(FullName,Name,ASIZE(FullName));
|
||||
SetName(FullName,LongName,ASIZE(FullName));
|
||||
|
||||
// Rename the existing file to randomly generated name. Normally it changes
|
||||
// the short name too.
|
||||
if (!MoveFile(FullName,NewName))
|
||||
return false;
|
||||
|
||||
// Now we need to create the temporary empty file with same name as
|
||||
// short name of our already existing file. We do it to occupy its previous
|
||||
// short name and not allow to use it again when renaming the file back to
|
||||
// its original long name.
|
||||
File KeepShortFile;
|
||||
bool Created=false;
|
||||
if (!FileExist(Name))
|
||||
Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
|
||||
|
||||
// Now we rename the existing file from temporary name to original long name.
|
||||
// Since its previous short name is occupied by another file, it should
|
||||
// get another short name.
|
||||
MoveFile(NewName,FullName);
|
||||
|
||||
if (Created)
|
||||
{
|
||||
// Delete the temporary zero length file occupying the short name,
|
||||
KeepShortFile.Close();
|
||||
KeepShortFile.Delete();
|
||||
}
|
||||
// We successfully changed the short name. Maybe sometimes we'll simplify
|
||||
// this function by use of SetFileShortName Windows API call.
|
||||
// But SetFileShortName is not available in older Windows.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef _RAR_FILECREATE_
|
||||
#define _RAR_FILECREATE_
|
||||
|
||||
bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
|
||||
bool *UserReject,int64 FileSize=INT64NDF,
|
||||
RarTime *FileTime=NULL,bool WriteOnly=false);
|
||||
|
||||
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize);
|
||||
|
||||
#if defined(_WIN_ALL)
|
||||
bool UpdateExistingShortName(const wchar *Name);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
729
unrar/file.cpp
729
unrar/file.cpp
@@ -1,729 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
File::File()
|
||||
{
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
*FileName=0;
|
||||
NewFile=false;
|
||||
LastWrite=false;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
IgnoreReadErrors=false;
|
||||
ErrorType=FILE_SUCCESS;
|
||||
OpenShared=false;
|
||||
AllowDelete=true;
|
||||
AllowExceptions=true;
|
||||
#ifdef _WIN_ALL
|
||||
NoSequentialRead=false;
|
||||
CreateMode=FMF_UNDEFINED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
if (hFile!=FILE_BAD_HANDLE && !SkipClose)
|
||||
if (NewFile)
|
||||
Delete();
|
||||
else
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
void File::operator = (File &SrcFile)
|
||||
{
|
||||
hFile=SrcFile.hFile;
|
||||
NewFile=SrcFile.NewFile;
|
||||
LastWrite=SrcFile.LastWrite;
|
||||
HandleType=SrcFile.HandleType;
|
||||
wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
|
||||
SrcFile.SkipClose=true;
|
||||
}
|
||||
|
||||
|
||||
bool File::Open(const wchar *Name,uint Mode)
|
||||
{
|
||||
ErrorType=FILE_SUCCESS;
|
||||
FileHandle hNewFile;
|
||||
bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
|
||||
bool UpdateMode=(Mode & FMF_UPDATE)!=0;
|
||||
bool WriteMode=(Mode & FMF_WRITE)!=0;
|
||||
#ifdef _WIN_ALL
|
||||
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
|
||||
if (UpdateMode)
|
||||
Access|=GENERIC_WRITE;
|
||||
uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
|
||||
if (OpenShared)
|
||||
ShareMode|=FILE_SHARE_WRITE;
|
||||
uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
|
||||
|
||||
DWORD LastError;
|
||||
if (hNewFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
LastError=GetLastError();
|
||||
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
{
|
||||
hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
|
||||
|
||||
// For archive names longer than 260 characters first CreateFile
|
||||
// (without \\?\) fails and sets LastError to 3 (access denied).
|
||||
// We need the correct "file not found" error code to decide
|
||||
// if we create a new archive or quit with "cannot create" error.
|
||||
// So we need to check the error code after \\?\ CreateFile again,
|
||||
// otherwise we'll fail to create new archives with long names.
|
||||
// But we cannot simply assign the new code to LastError,
|
||||
// because it would break "..\arcname.rar" relative names processing.
|
||||
// First CreateFile returns the correct "file not found" code for such
|
||||
// names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
|
||||
// dots as a directory name. So we check only for "file not found"
|
||||
// error here and for other errors use the first CreateFile result.
|
||||
if (GetLastError()==ERROR_FILE_NOT_FOUND)
|
||||
LastError=ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
|
||||
ErrorType=FILE_NOTFOUND;
|
||||
|
||||
#else
|
||||
int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
|
||||
#ifdef O_BINARY
|
||||
flags|=O_BINARY;
|
||||
#if defined(_AIX) && defined(_LARGE_FILE_API)
|
||||
flags|=O_LARGEFILE;
|
||||
#endif
|
||||
#endif
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
|
||||
int handle=open(NameA,flags);
|
||||
#ifdef LOCK_EX
|
||||
|
||||
#ifdef _OSF_SOURCE
|
||||
extern "C" int flock(int, int);
|
||||
#endif
|
||||
|
||||
if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
|
||||
{
|
||||
close(handle);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (handle==-1)
|
||||
hNewFile=FILE_BAD_HANDLE;
|
||||
else
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
hNewFile=handle;
|
||||
#else
|
||||
hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
|
||||
#endif
|
||||
}
|
||||
if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
|
||||
ErrorType=FILE_NOTFOUND;
|
||||
#endif
|
||||
NewFile=false;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
bool Success=hNewFile!=FILE_BAD_HANDLE;
|
||||
if (Success)
|
||||
{
|
||||
hFile=hNewFile;
|
||||
wcsncpyz(FileName,Name,ASIZE(FileName));
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void File::TOpen(const wchar *Name)
|
||||
{
|
||||
if (!WOpen(Name))
|
||||
ErrHandler.Exit(RARX_OPEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool File::WOpen(const wchar *Name)
|
||||
{
|
||||
if (Open(Name))
|
||||
return true;
|
||||
ErrHandler.OpenErrorMsg(Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool File::Create(const wchar *Name,uint Mode)
|
||||
{
|
||||
// OpenIndiana based NAS and CIFS shares fail to set the file time if file
|
||||
// was created in read+write mode and some data was written and not flushed
|
||||
// before SetFileTime call. So we should use the write only mode if we plan
|
||||
// SetFileTime call and do not need to read from file.
|
||||
bool WriteMode=(Mode & FMF_WRITE)!=0;
|
||||
bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
|
||||
#ifdef _WIN_ALL
|
||||
CreateMode=Mode;
|
||||
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
|
||||
DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
|
||||
|
||||
// Windows automatically removes dots and spaces in the end of file name,
|
||||
// So we detect such names and process them with \\?\ prefix.
|
||||
wchar *LastChar=PointToLastChar(Name);
|
||||
bool Special=*LastChar=='.' || *LastChar==' ';
|
||||
|
||||
if (Special && (Mode & FMF_STANDARDNAMES)==0)
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
else
|
||||
hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
|
||||
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
|
||||
#else
|
||||
hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
|
||||
#endif
|
||||
#endif
|
||||
NewFile=true;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
wcsncpyz(FileName,Name,ASIZE(FileName));
|
||||
return hFile!=FILE_BAD_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void File::TCreate(const wchar *Name,uint Mode)
|
||||
{
|
||||
if (!WCreate(Name,Mode))
|
||||
ErrHandler.Exit(RARX_FATAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool File::WCreate(const wchar *Name,uint Mode)
|
||||
{
|
||||
if (Create(Name,Mode))
|
||||
return true;
|
||||
ErrHandler.CreateErrorMsg(Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool File::Close()
|
||||
{
|
||||
bool Success=true;
|
||||
|
||||
if (hFile!=FILE_BAD_HANDLE)
|
||||
{
|
||||
if (!SkipClose)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We use the standard system handle for stdout in Windows
|
||||
// and it must not be closed here.
|
||||
if (HandleType==FILE_HANDLENORMAL)
|
||||
Success=CloseHandle(hFile)==TRUE;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
Success=close(hFile)!=-1;
|
||||
#else
|
||||
Success=fclose(hFile)!=EOF;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
}
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
if (!Success && AllowExceptions)
|
||||
ErrHandler.CloseError(FileName);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool File::Delete()
|
||||
{
|
||||
if (HandleType!=FILE_HANDLENORMAL)
|
||||
return false;
|
||||
if (hFile!=FILE_BAD_HANDLE)
|
||||
Close();
|
||||
if (!AllowDelete)
|
||||
return false;
|
||||
return DelFile(FileName);
|
||||
}
|
||||
|
||||
|
||||
bool File::Rename(const wchar *NewName)
|
||||
{
|
||||
// No need to rename if names are already same.
|
||||
bool Success=wcscmp(FileName,NewName)==0;
|
||||
|
||||
if (!Success)
|
||||
Success=RenameFile(FileName,NewName);
|
||||
|
||||
if (Success)
|
||||
wcscpy(FileName,NewName);
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool File::Write(const void *Data,size_t Size)
|
||||
{
|
||||
if (Size==0)
|
||||
return true;
|
||||
if (HandleType==FILE_HANDLESTD)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
hFile=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#else
|
||||
// Cannot use the standard stdout here, because it already has wide orientation.
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=dup(STDOUT_FILENO); // Open new stdout stream.
|
||||
#else
|
||||
hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool Success;
|
||||
while (1)
|
||||
{
|
||||
Success=false;
|
||||
#ifdef _WIN_ALL
|
||||
DWORD Written=0;
|
||||
if (HandleType!=FILE_HANDLENORMAL)
|
||||
{
|
||||
// writing to stdout can fail in old Windows if data block is too large
|
||||
const size_t MaxSize=0x4000;
|
||||
for (size_t I=0;I<Size;I+=MaxSize)
|
||||
{
|
||||
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE;
|
||||
if (!Success)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
ssize_t Written=write(hFile,Data,Size);
|
||||
Success=Written==Size;
|
||||
#else
|
||||
int Written=fwrite(Data,1,Size,hFile);
|
||||
Success=Written==Size && !ferror(hFile);
|
||||
#endif
|
||||
#endif
|
||||
if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL)
|
||||
{
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
|
||||
int ErrCode=GetLastError();
|
||||
int64 FilePos=Tell();
|
||||
uint64 FreeSize=GetFreeDisk(FileName);
|
||||
SetLastError(ErrCode);
|
||||
if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
|
||||
ErrHandler.WriteErrorFAT(FileName);
|
||||
#endif
|
||||
if (ErrHandler.AskRepeatWrite(FileName,false))
|
||||
{
|
||||
#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
|
||||
clearerr(hFile);
|
||||
#endif
|
||||
if (Written<Size && Written>0)
|
||||
Seek(Tell()-Written,SEEK_SET);
|
||||
continue;
|
||||
}
|
||||
ErrHandler.WriteError(NULL,FileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LastWrite=true;
|
||||
return Success; // It can return false only if AllowExceptions is disabled.
|
||||
}
|
||||
|
||||
|
||||
int File::Read(void *Data,size_t Size)
|
||||
{
|
||||
int64 FilePos=0; // Initialized only to suppress some compilers warning.
|
||||
|
||||
if (IgnoreReadErrors)
|
||||
FilePos=Tell();
|
||||
int ReadSize;
|
||||
while (true)
|
||||
{
|
||||
ReadSize=DirectRead(Data,Size);
|
||||
if (ReadSize==-1)
|
||||
{
|
||||
ErrorType=FILE_READERROR;
|
||||
if (AllowExceptions)
|
||||
if (IgnoreReadErrors)
|
||||
{
|
||||
ReadSize=0;
|
||||
for (size_t I=0;I<Size;I+=512)
|
||||
{
|
||||
Seek(FilePos+I,SEEK_SET);
|
||||
size_t SizeToRead=Min(Size-I,512);
|
||||
int ReadCode=DirectRead(Data,SizeToRead);
|
||||
ReadSize+=(ReadCode==-1) ? 512:ReadCode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HandleType==FILE_HANDLENORMAL && ErrHandler.AskRepeatRead(FileName))
|
||||
continue;
|
||||
ErrHandler.ReadError(FileName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ReadSize;
|
||||
}
|
||||
|
||||
|
||||
// Returns -1 in case of error.
|
||||
int File::DirectRead(void *Data,size_t Size)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
const size_t MaxDeviceRead=20000;
|
||||
const size_t MaxLockedRead=32768;
|
||||
#endif
|
||||
if (HandleType==FILE_HANDLESTD)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// if (Size>MaxDeviceRead)
|
||||
// Size=MaxDeviceRead;
|
||||
hFile=GetStdHandle(STD_INPUT_HANDLE);
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=STDIN_FILENO;
|
||||
#else
|
||||
hFile=stdin;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
// For pipes like 'type file.txt | rar -si arcname' ReadFile may return
|
||||
// data in small ~4KB blocks. It may slightly reduce the compression ratio.
|
||||
DWORD Read;
|
||||
if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
|
||||
{
|
||||
if (IsDevice() && Size>MaxDeviceRead)
|
||||
return DirectRead(Data,MaxDeviceRead);
|
||||
if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
|
||||
return 0;
|
||||
|
||||
// We had a bug report about failure to archive 1C database lock file
|
||||
// 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
|
||||
// permanently locked. If our first read request uses too large buffer
|
||||
// and if we are in -dh mode, so we were able to open the file,
|
||||
// we'll fail with "Read error". So now we use try a smaller buffer size
|
||||
// in case of lock error.
|
||||
if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
|
||||
GetLastError()==ERROR_LOCK_VIOLATION)
|
||||
return DirectRead(Data,MaxLockedRead);
|
||||
|
||||
return -1;
|
||||
}
|
||||
return Read;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
ssize_t ReadSize=read(hFile,Data,Size);
|
||||
if (ReadSize==-1)
|
||||
return -1;
|
||||
return (int)ReadSize;
|
||||
#else
|
||||
if (LastWrite)
|
||||
{
|
||||
fflush(hFile);
|
||||
LastWrite=false;
|
||||
}
|
||||
clearerr(hFile);
|
||||
size_t ReadSize=fread(Data,1,Size,hFile);
|
||||
if (ferror(hFile))
|
||||
return -1;
|
||||
return (int)ReadSize;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Seek(int64 Offset,int Method)
|
||||
{
|
||||
if (!RawSeek(Offset,Method) && AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
}
|
||||
|
||||
|
||||
bool File::RawSeek(int64 Offset,int Method)
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
return true;
|
||||
if (Offset<0 && Method!=SEEK_SET)
|
||||
{
|
||||
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
|
||||
Method=SEEK_SET;
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
LONG HighDist=(LONG)(Offset>>32);
|
||||
if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
|
||||
GetLastError()!=NO_ERROR)
|
||||
return false;
|
||||
#else
|
||||
LastWrite=false;
|
||||
#ifdef FILE_USE_OPEN
|
||||
if (lseek(hFile,(off_t)Offset,Method)==-1)
|
||||
return false;
|
||||
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
|
||||
if (fseeko(hFile,Offset,Method)!=0)
|
||||
return false;
|
||||
#else
|
||||
if (fseek(hFile,(long)Offset,Method)!=0)
|
||||
return false;
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int64 File::Tell()
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
if (AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
else
|
||||
return -1;
|
||||
#ifdef _WIN_ALL
|
||||
LONG HighDist=0;
|
||||
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
|
||||
if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
|
||||
if (AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
else
|
||||
return -1;
|
||||
return INT32TO64(HighDist,LowDist);
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
return lseek(hFile,0,SEEK_CUR);
|
||||
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
|
||||
return ftello(hFile);
|
||||
#else
|
||||
return ftell(hFile);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Prealloc(int64 Size)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
if (RawSeek(Size,SEEK_SET))
|
||||
{
|
||||
Truncate();
|
||||
Seek(0,SEEK_SET);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_UNIX) && defined(USE_FALLOCATE)
|
||||
// fallocate is rather new call. Only latest kernels support it.
|
||||
// So we are not using it by default yet.
|
||||
int fd = GetFD();
|
||||
if (fd >= 0)
|
||||
fallocate(fd, 0, 0, Size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
byte File::GetByte()
|
||||
{
|
||||
byte Byte=0;
|
||||
Read(&Byte,1);
|
||||
return Byte;
|
||||
}
|
||||
|
||||
|
||||
void File::PutByte(byte Byte)
|
||||
{
|
||||
Write(&Byte,1);
|
||||
}
|
||||
|
||||
|
||||
bool File::Truncate()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return SetEndOfFile(hFile)==TRUE;
|
||||
#else
|
||||
return ftruncate(GetFD(),(off_t)Tell())==0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Flush()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
FlushFileBuffers(hFile);
|
||||
#else
|
||||
#ifndef FILE_USE_OPEN
|
||||
fflush(hFile);
|
||||
#endif
|
||||
fsync(GetFD());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// Workaround for OpenIndiana NAS time bug. If we cannot create a file
|
||||
// in write only mode, we need to flush the write buffer before calling
|
||||
// SetFileTime or file time will not be changed.
|
||||
if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
|
||||
FlushFileBuffers(hFile);
|
||||
|
||||
bool sm=ftm!=NULL && ftm->IsSet();
|
||||
bool sc=ftc!=NULL && ftc->IsSet();
|
||||
bool sa=fta!=NULL && fta->IsSet();
|
||||
FILETIME fm,fc,fa;
|
||||
if (sm)
|
||||
ftm->GetWinFT(&fm);
|
||||
if (sc)
|
||||
ftc->GetWinFT(&fc);
|
||||
if (sa)
|
||||
fta->GetWinFT(&fa);
|
||||
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
|
||||
{
|
||||
// Android APP_PLATFORM := android-14 does not support futimens and futimes.
|
||||
// Newer platforms support futimens, but fail on Android 4.2.
|
||||
// We have to use utime for Android.
|
||||
// Also we noticed futimens fail to set timestamps on NTFS partition
|
||||
// mounted to virtual Linux x86 machine, but utimensat worked correctly.
|
||||
// So we set timestamps for already closed files in Unix.
|
||||
#ifdef _UNIX
|
||||
SetCloseFileTimeByName(FileName,ftm,fta);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
bool setm=ftm!=NULL && ftm->IsSet();
|
||||
bool seta=fta!=NULL && fta->IsSet();
|
||||
if (setm || seta)
|
||||
{
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
|
||||
#ifdef UNIX_TIME_NS
|
||||
timespec times[2];
|
||||
times[0].tv_sec=seta ? fta->GetUnix() : 0;
|
||||
times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
|
||||
times[1].tv_sec=setm ? ftm->GetUnix() : 0;
|
||||
times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
|
||||
utimensat(AT_FDCWD,NameA,times,0);
|
||||
#else
|
||||
utimbuf ut;
|
||||
if (setm)
|
||||
ut.modtime=ftm->GetUnix();
|
||||
else
|
||||
ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
|
||||
if (seta)
|
||||
ut.actime=fta->GetUnix();
|
||||
else
|
||||
ut.actime=ut.modtime; // Need to set something, cannot left it 0.
|
||||
utime(NameA,&ut);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::GetOpenFileTime(RarTime *ft)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
FILETIME FileTime;
|
||||
GetFileTime(hFile,NULL,NULL,&FileTime);
|
||||
ft->SetWinFT(&FileTime);
|
||||
#endif
|
||||
#if defined(_UNIX) || defined(_EMX)
|
||||
struct stat st;
|
||||
fstat(GetFD(),&st);
|
||||
ft->SetUnix(st.st_mtime);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int64 File::FileLength()
|
||||
{
|
||||
SaveFilePos SavePos(*this);
|
||||
Seek(0,SEEK_END);
|
||||
return Tell();
|
||||
}
|
||||
|
||||
|
||||
bool File::IsDevice()
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
return false;
|
||||
#ifdef _WIN_ALL
|
||||
uint Type=GetFileType(hFile);
|
||||
return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
|
||||
#else
|
||||
return isatty(GetFD());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 File::Copy(File &Dest,int64 Length)
|
||||
{
|
||||
Array<char> Buffer(0x40000);
|
||||
int64 CopySize=0;
|
||||
bool CopyAll=(Length==INT64NDF);
|
||||
|
||||
while (CopyAll || Length>0)
|
||||
{
|
||||
Wait();
|
||||
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
|
||||
char *Buf=&Buffer[0];
|
||||
int ReadSize=Read(Buf,SizeToRead);
|
||||
if (ReadSize==0)
|
||||
break;
|
||||
size_t WriteSize=ReadSize;
|
||||
#ifdef _WIN_ALL
|
||||
// For FAT32 USB flash drives in Windows if first write is 4 KB or more,
|
||||
// write caching is disabled and "write through" is enabled, resulting
|
||||
// in bad performance, especially for many small files. It happens when
|
||||
// we create SFX archive on USB drive, because SFX module is written first.
|
||||
// So we split the first write to small 1 KB followed by rest of data.
|
||||
if (CopySize==0 && WriteSize>=4096)
|
||||
{
|
||||
const size_t FirstWrite=1024;
|
||||
Dest.Write(Buf,FirstWrite);
|
||||
Buf+=FirstWrite;
|
||||
WriteSize-=FirstWrite;
|
||||
}
|
||||
#endif
|
||||
Dest.Write(Buf,WriteSize);
|
||||
CopySize+=ReadSize;
|
||||
if (!CopyAll)
|
||||
Length-=ReadSize;
|
||||
}
|
||||
return CopySize;
|
||||
}
|
||||
#endif
|
||||
126
unrar/file.hpp
126
unrar/file.hpp
@@ -1,126 +0,0 @@
|
||||
#ifndef _RAR_FILE_
|
||||
#define _RAR_FILE_
|
||||
|
||||
#define FILE_USE_OPEN
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
typedef HANDLE FileHandle;
|
||||
#define FILE_BAD_HANDLE INVALID_HANDLE_VALUE
|
||||
#elif defined(FILE_USE_OPEN)
|
||||
typedef off_t FileHandle;
|
||||
#define FILE_BAD_HANDLE -1
|
||||
#else
|
||||
typedef FILE* FileHandle;
|
||||
#define FILE_BAD_HANDLE NULL
|
||||
#endif
|
||||
|
||||
class RAROptions;
|
||||
|
||||
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
|
||||
|
||||
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
|
||||
|
||||
enum FILE_MODE_FLAGS {
|
||||
// Request read only access to file. Default for Open.
|
||||
FMF_READ=0,
|
||||
|
||||
// Request both read and write access to file. Default for Create.
|
||||
FMF_UPDATE=1,
|
||||
|
||||
// Request write only access to file.
|
||||
FMF_WRITE=2,
|
||||
|
||||
// Open files which are already opened for write by other programs.
|
||||
FMF_OPENSHARED=4,
|
||||
|
||||
// Open files only if no other program is opened it even in shared mode.
|
||||
FMF_OPENEXCLUSIVE=8,
|
||||
|
||||
// Provide read access to created file for other programs.
|
||||
FMF_SHAREREAD=16,
|
||||
|
||||
// Use standard NTFS names without trailing dots and spaces.
|
||||
FMF_STANDARDNAMES=32,
|
||||
|
||||
// Mode flags are not defined yet.
|
||||
FMF_UNDEFINED=256
|
||||
};
|
||||
|
||||
|
||||
class File
|
||||
{
|
||||
private:
|
||||
FileHandle hFile;
|
||||
bool LastWrite;
|
||||
FILE_HANDLETYPE HandleType;
|
||||
bool SkipClose;
|
||||
bool IgnoreReadErrors;
|
||||
bool NewFile;
|
||||
bool AllowDelete;
|
||||
bool AllowExceptions;
|
||||
#ifdef _WIN_ALL
|
||||
bool NoSequentialRead;
|
||||
uint CreateMode;
|
||||
#endif
|
||||
protected:
|
||||
bool OpenShared; // Set by 'Archive' class.
|
||||
public:
|
||||
wchar FileName[NM];
|
||||
|
||||
FILE_ERRORTYPE ErrorType;
|
||||
public:
|
||||
File();
|
||||
virtual ~File();
|
||||
void operator = (File &SrcFile);
|
||||
virtual bool Open(const wchar *Name,uint Mode=FMF_READ);
|
||||
void TOpen(const wchar *Name);
|
||||
bool WOpen(const wchar *Name);
|
||||
bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
|
||||
void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
|
||||
bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
|
||||
bool Close();
|
||||
bool Delete();
|
||||
bool Rename(const wchar *NewName);
|
||||
bool Write(const void *Data,size_t Size);
|
||||
virtual int Read(void *Data,size_t Size);
|
||||
int DirectRead(void *Data,size_t Size);
|
||||
virtual void Seek(int64 Offset,int Method);
|
||||
bool RawSeek(int64 Offset,int Method);
|
||||
virtual int64 Tell();
|
||||
void Prealloc(int64 Size);
|
||||
byte GetByte();
|
||||
void PutByte(byte Byte);
|
||||
bool Truncate();
|
||||
void Flush();
|
||||
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
|
||||
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
|
||||
static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
|
||||
void GetOpenFileTime(RarTime *ft);
|
||||
bool IsOpened() {return hFile!=FILE_BAD_HANDLE;};
|
||||
int64 FileLength();
|
||||
void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
|
||||
FILE_HANDLETYPE GetHandleType() {return HandleType;}
|
||||
bool IsDevice();
|
||||
static bool RemoveCreated();
|
||||
FileHandle GetHandle() {return hFile;}
|
||||
void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
|
||||
void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}
|
||||
int64 Copy(File &Dest,int64 Length=INT64NDF);
|
||||
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
|
||||
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
|
||||
#ifdef _WIN_ALL
|
||||
void RemoveSequentialFlag() {NoSequentialRead=true;}
|
||||
#endif
|
||||
#ifdef _UNIX
|
||||
int GetFD()
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
return hFile;
|
||||
#else
|
||||
return fileno(hFile);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
510
unrar/filefn.cpp
510
unrar/filefn.cpp
@@ -1,510 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// Windows automatically removes dots and spaces in the end of directory
|
||||
// name. So we detect such names and process them with \\?\ prefix.
|
||||
wchar *LastChar=PointToLastChar(Name);
|
||||
bool Special=*LastChar=='.' || *LastChar==' ';
|
||||
BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
|
||||
if (RetCode==0 && !FileExist(Name))
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
RetCode=CreateDirectory(LongName,NULL);
|
||||
}
|
||||
if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
|
||||
{
|
||||
if (SetAttr)
|
||||
SetFileAttr(Name,Attr);
|
||||
return MKDIR_SUCCESS;
|
||||
}
|
||||
int ErrCode=GetLastError();
|
||||
if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
|
||||
return MKDIR_BADPATH;
|
||||
return MKDIR_ERROR;
|
||||
#elif defined(_UNIX)
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
mode_t uattr=SetAttr ? (mode_t)Attr:0777;
|
||||
int ErrCode=mkdir(NameA,uattr);
|
||||
if (ErrCode==-1)
|
||||
return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
|
||||
return MKDIR_SUCCESS;
|
||||
#else
|
||||
return MKDIR_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool CreatePath(const wchar *Path,bool SkipLastName)
|
||||
{
|
||||
if (Path==NULL || *Path==0)
|
||||
return false;
|
||||
|
||||
#if defined(_WIN_ALL) || defined(_EMX)
|
||||
uint DirAttr=0;
|
||||
#else
|
||||
uint DirAttr=0777;
|
||||
#endif
|
||||
|
||||
bool Success=true;
|
||||
|
||||
for (const wchar *s=Path;*s!=0;s++)
|
||||
{
|
||||
wchar DirName[NM];
|
||||
if (s-Path>=ASIZE(DirName))
|
||||
break;
|
||||
|
||||
// Process all kinds of path separators, so user can enter Unix style
|
||||
// path in Windows or Windows in Unix. s>Path check avoids attempting
|
||||
// creating an empty directory for paths starting from path separator.
|
||||
if (IsPathDiv(*s) && s>Path)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We must not attempt to create "D:" directory, because first
|
||||
// CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
|
||||
// to create "D:" directory.
|
||||
if (s==Path+2 && Path[1]==':')
|
||||
continue;
|
||||
#endif
|
||||
wcsncpy(DirName,Path,s-Path);
|
||||
DirName[s-Path]=0;
|
||||
|
||||
Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
|
||||
if (Success)
|
||||
{
|
||||
mprintf(St(MCreatDir),DirName);
|
||||
mprintf(L" %s",St(MOk));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
|
||||
Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
#if defined(_WIN_ALL)
|
||||
bool sm=ftm!=NULL && ftm->IsSet();
|
||||
bool sc=ftc!=NULL && ftc->IsSet();
|
||||
bool sa=fta!=NULL && fta->IsSet();
|
||||
|
||||
uint DirAttr=GetFileAttr(Name);
|
||||
bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
|
||||
if (ResetAttr)
|
||||
SetFileAttr(Name,0);
|
||||
|
||||
HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
|
||||
}
|
||||
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
FILETIME fm,fc,fa;
|
||||
if (sm)
|
||||
ftm->GetWinFT(&fm);
|
||||
if (sc)
|
||||
ftc->GetWinFT(&fc);
|
||||
if (sa)
|
||||
fta->GetWinFT(&fa);
|
||||
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
|
||||
CloseHandle(hFile);
|
||||
if (ResetAttr)
|
||||
SetFileAttr(Name,DirAttr);
|
||||
#endif
|
||||
#if defined(_UNIX) || defined(_EMX)
|
||||
File::SetCloseFileTimeByName(Name,ftm,fta);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool IsRemovable(const wchar *Name)
|
||||
{
|
||||
#if defined(_WIN_ALL)
|
||||
wchar Root[NM];
|
||||
GetPathRoot(Name,Root,ASIZE(Root));
|
||||
int Type=GetDriveType(*Root!=0 ? Root:NULL);
|
||||
return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 GetFreeDisk(const wchar *Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
wchar Root[NM];
|
||||
GetFilePath(Name,Root,ASIZE(Root));
|
||||
|
||||
ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
|
||||
uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
|
||||
if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
|
||||
uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
|
||||
return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
|
||||
return 0;
|
||||
#elif defined(_UNIX)
|
||||
wchar Root[NM];
|
||||
GetFilePath(Name,Root,ASIZE(Root));
|
||||
char RootA[NM];
|
||||
WideToChar(Root,RootA,ASIZE(RootA));
|
||||
struct statvfs sfs;
|
||||
if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
|
||||
return 0;
|
||||
int64 FreeSize=sfs.f_bsize;
|
||||
FreeSize=FreeSize*sfs.f_bavail;
|
||||
return FreeSize;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
|
||||
// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
|
||||
// file size to 4 GB for these file systems.
|
||||
bool IsFAT(const wchar *Name)
|
||||
{
|
||||
wchar Root[NM];
|
||||
GetPathRoot(Name,Root,ASIZE(Root));
|
||||
wchar FileSystem[MAX_PATH+1];
|
||||
if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
|
||||
return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool FileExist(const wchar *Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return GetFileAttr(Name)!=0xffffffff;
|
||||
#elif defined(ENABLE_ACCESS)
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
return access(NameA,0)==0;
|
||||
#else
|
||||
FindData FD;
|
||||
return FindFile::FastFind(Name,&FD);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool WildFileExist(const wchar *Name)
|
||||
{
|
||||
if (IsWildcard(Name))
|
||||
{
|
||||
FindFile Find;
|
||||
Find.SetMask(Name);
|
||||
FindData fd;
|
||||
return Find.Next(&fd);
|
||||
}
|
||||
return FileExist(Name);
|
||||
}
|
||||
|
||||
|
||||
bool IsDir(uint Attr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
|
||||
#endif
|
||||
#if defined(_UNIX)
|
||||
return (Attr & 0xF000)==0x4000;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool IsUnreadable(uint Attr)
|
||||
{
|
||||
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
|
||||
return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IsLink(uint Attr)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
return (Attr & 0xF000)==0xA000;
|
||||
#elif defined(_WIN_ALL)
|
||||
return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsDeleteAllowed(uint FileAttr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
|
||||
#else
|
||||
return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PrepareToDelete(const wchar *Name)
|
||||
{
|
||||
#if defined(_WIN_ALL) || defined(_EMX)
|
||||
SetFileAttr(Name,0);
|
||||
#endif
|
||||
#ifdef _UNIX
|
||||
if (Name!=NULL)
|
||||
{
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint GetFileAttr(const wchar *Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
DWORD Attr=GetFileAttributes(Name);
|
||||
if (Attr==0xffffffff)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
Attr=GetFileAttributes(LongName);
|
||||
}
|
||||
return Attr;
|
||||
#else
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
struct stat st;
|
||||
if (stat(NameA,&st)!=0)
|
||||
return 0;
|
||||
return st.st_mode;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool SetFileAttr(const wchar *Name,uint Attr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=SetFileAttributes(Name,Attr)!=0;
|
||||
if (!Success)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
Success=SetFileAttributes(LongName,Attr)!=0;
|
||||
}
|
||||
return Success;
|
||||
#elif defined(_UNIX)
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
return chmod(NameA,(mode_t)Attr)==0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
wchar *MkTemp(wchar *Name,size_t MaxSize)
|
||||
{
|
||||
size_t Length=wcslen(Name);
|
||||
|
||||
RarTime CurTime;
|
||||
CurTime.SetCurrentTime();
|
||||
|
||||
// We cannot use CurTime.GetWin() as is, because its lowest bits can
|
||||
// have low informational value, like being a zero or few fixed numbers.
|
||||
uint Random=(uint)(CurTime.GetWin()/100000);
|
||||
|
||||
// Using PID we guarantee that different RAR copies use different temp names
|
||||
// even if started in exactly the same time.
|
||||
uint PID=0;
|
||||
#ifdef _WIN_ALL
|
||||
PID=(uint)GetCurrentProcessId();
|
||||
#elif defined(_UNIX)
|
||||
PID=(uint)getpid();
|
||||
#endif
|
||||
|
||||
for (uint Attempt=0;;Attempt++)
|
||||
{
|
||||
uint Ext=Random%50000+Attempt;
|
||||
wchar RndText[50];
|
||||
swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
|
||||
if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
|
||||
return NULL;
|
||||
wcscpy(Name+Length,RndText);
|
||||
if (!FileExist(Name))
|
||||
break;
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
|
||||
{
|
||||
SaveFilePos SavePos(*SrcFile);
|
||||
#ifndef SILENT
|
||||
int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
|
||||
#endif
|
||||
|
||||
if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
|
||||
uiMsg(UIEVENT_FILESUMSTART);
|
||||
|
||||
if ((Flags & CALCFSUM_CURPOS)==0)
|
||||
SrcFile->Seek(0,SEEK_SET);
|
||||
|
||||
const size_t BufSize=0x100000;
|
||||
Array<byte> Data(BufSize);
|
||||
|
||||
|
||||
DataHash HashCRC,HashBlake2;
|
||||
HashCRC.Init(HASH_CRC32,Threads);
|
||||
HashBlake2.Init(HASH_BLAKE2,Threads);
|
||||
|
||||
int64 BlockCount=0;
|
||||
int64 TotalRead=0;
|
||||
while (true)
|
||||
{
|
||||
size_t SizeToRead;
|
||||
if (Size==INT64NDF) // If we process the entire file.
|
||||
SizeToRead=BufSize; // Then always attempt to read the entire buffer.
|
||||
else
|
||||
SizeToRead=(size_t)Min((int64)BufSize,Size);
|
||||
int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
|
||||
if (ReadSize==0)
|
||||
break;
|
||||
TotalRead+=ReadSize;
|
||||
|
||||
if ((++BlockCount & 0xf)==0)
|
||||
{
|
||||
#ifndef SILENT
|
||||
if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
|
||||
uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength);
|
||||
else
|
||||
{
|
||||
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
|
||||
uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
|
||||
}
|
||||
#endif
|
||||
Wait();
|
||||
}
|
||||
|
||||
if (CRC32!=NULL)
|
||||
HashCRC.Update(&Data[0],ReadSize);
|
||||
if (Blake2!=NULL)
|
||||
HashBlake2.Update(&Data[0],ReadSize);
|
||||
|
||||
if (Size!=INT64NDF)
|
||||
Size-=ReadSize;
|
||||
}
|
||||
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
|
||||
uiMsg(UIEVENT_FILESUMEND);
|
||||
|
||||
if (CRC32!=NULL)
|
||||
*CRC32=HashCRC.GetCRC32();
|
||||
if (Blake2!=NULL)
|
||||
{
|
||||
HashValue Result;
|
||||
HashBlake2.Result(&Result);
|
||||
memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool RenameFile(const wchar *SrcName,const wchar *DestName)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=MoveFile(SrcName,DestName)!=0;
|
||||
if (!Success)
|
||||
{
|
||||
wchar LongName1[NM],LongName2[NM];
|
||||
if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
|
||||
GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
|
||||
Success=MoveFile(LongName1,LongName2)!=0;
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
char SrcNameA[NM],DestNameA[NM];
|
||||
WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
|
||||
WideToChar(DestName,DestNameA,ASIZE(DestNameA));
|
||||
bool Success=rename(SrcNameA,DestNameA)==0;
|
||||
return Success;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool DelFile(const wchar *Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=DeleteFile(Name)!=0;
|
||||
if (!Success)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
Success=DeleteFile(LongName)!=0;
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
bool Success=remove(NameA)==0;
|
||||
return Success;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
bool SetFileCompression(const wchar *Name,bool State)
|
||||
{
|
||||
HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
||||
}
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
|
||||
DWORD Result;
|
||||
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
|
||||
sizeof(NewState),NULL,0,&Result,NULL);
|
||||
CloseHandle(hFile);
|
||||
return RetCode!=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef _RAR_FILEFN_
|
||||
#define _RAR_FILEFN_
|
||||
|
||||
enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
|
||||
|
||||
MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr);
|
||||
bool CreatePath(const wchar *Path,bool SkipLastName);
|
||||
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
|
||||
bool IsRemovable(const wchar *Name);
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 GetFreeDisk(const wchar *Name);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
|
||||
bool IsFAT(const wchar *Root);
|
||||
#endif
|
||||
|
||||
bool FileExist(const wchar *Name);
|
||||
bool WildFileExist(const wchar *Name);
|
||||
bool IsDir(uint Attr);
|
||||
bool IsUnreadable(uint Attr);
|
||||
bool IsLink(uint Attr);
|
||||
void SetSFXMode(const wchar *FileName);
|
||||
void EraseDiskContents(const wchar *FileName);
|
||||
bool IsDeleteAllowed(uint FileAttr);
|
||||
void PrepareToDelete(const wchar *Name);
|
||||
uint GetFileAttr(const wchar *Name);
|
||||
bool SetFileAttr(const wchar *Name,uint Attr);
|
||||
#if 0
|
||||
wchar* MkTemp(wchar *Name,size_t MaxSize);
|
||||
#endif
|
||||
|
||||
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
|
||||
|
||||
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
|
||||
|
||||
bool RenameFile(const wchar *SrcName,const wchar *DestName);
|
||||
bool DelFile(const wchar *Name);
|
||||
bool DelDir(const wchar *Name);
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
bool SetFileCompression(const wchar *Name,bool State);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,162 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
bool ReadTextFile(
|
||||
const wchar *Name,
|
||||
StringList *List,
|
||||
bool Config,
|
||||
bool AbortOnError,
|
||||
RAR_CHARSET SrcCharset,
|
||||
bool Unquote,
|
||||
bool SkipComments,
|
||||
bool ExpandEnvStr)
|
||||
{
|
||||
wchar FileName[NM];
|
||||
*FileName=0;
|
||||
|
||||
if (Name!=NULL)
|
||||
if (Config)
|
||||
GetConfigName(Name,FileName,ASIZE(FileName),true,false);
|
||||
else
|
||||
wcsncpyz(FileName,Name,ASIZE(FileName));
|
||||
|
||||
File SrcFile;
|
||||
if (*FileName!=0)
|
||||
{
|
||||
bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
|
||||
|
||||
if (!OpenCode)
|
||||
{
|
||||
if (AbortOnError)
|
||||
ErrHandler.Exit(RARX_OPEN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
SrcFile.SetHandleType(FILE_HANDLESTD);
|
||||
|
||||
uint DataSize=0,ReadSize;
|
||||
const int ReadBlock=4096;
|
||||
|
||||
Array<byte> Data(ReadBlock);
|
||||
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
|
||||
{
|
||||
DataSize+=ReadSize;
|
||||
Data.Add(ReadSize); // Always have ReadBlock available for next data.
|
||||
}
|
||||
// Set to really read size, so we can zero terminate it correctly.
|
||||
Data.Alloc(DataSize);
|
||||
|
||||
int LowEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0;
|
||||
int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0;
|
||||
bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf;
|
||||
|
||||
if (SrcCharset==RCH_DEFAULT)
|
||||
{
|
||||
if (LowEndian || BigEndian)
|
||||
for (size_t I=2;I<DataSize;I++)
|
||||
if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n')
|
||||
{
|
||||
SrcCharset=RCH_UNICODE; // High byte in UTF-16 char is found.
|
||||
break;
|
||||
}
|
||||
if (Utf8)
|
||||
{
|
||||
Data.Push(0); // Need a zero terminated string for UtfToWide.
|
||||
if (IsTextUtf8((const char *)(Data+3)))
|
||||
SrcCharset=RCH_UTF8;
|
||||
}
|
||||
}
|
||||
|
||||
Array<wchar> DataW;
|
||||
|
||||
if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
|
||||
{
|
||||
Data.Push(0); // Zero terminate.
|
||||
#if defined(_WIN_ALL)
|
||||
if (SrcCharset==RCH_OEM)
|
||||
OemToCharA((char *)&Data[0],(char *)&Data[0]);
|
||||
#endif
|
||||
DataW.Alloc(Data.Size());
|
||||
CharToWide((char *)&Data[0],&DataW[0],DataW.Size());
|
||||
}
|
||||
|
||||
if (SrcCharset==RCH_UNICODE)
|
||||
{
|
||||
size_t Start=2; // Skip byte order mark.
|
||||
if (!LowEndian && !BigEndian) // No byte order mask.
|
||||
{
|
||||
Start=0;
|
||||
LowEndian=1;
|
||||
}
|
||||
|
||||
DataW.Alloc(Data.Size()/2+1);
|
||||
size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16.
|
||||
for (size_t I=Start;I<End;I+=2)
|
||||
DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LowEndian]*256;
|
||||
DataW[(End-Start)/2]=0;
|
||||
}
|
||||
|
||||
if (SrcCharset==RCH_UTF8)
|
||||
{
|
||||
Data.Push(0); // Zero terminate data.
|
||||
DataW.Alloc(Data.Size());
|
||||
UtfToWide((const char *)(Data+(Utf8 ? 3:0)),&DataW[0],DataW.Size());
|
||||
}
|
||||
|
||||
wchar *CurStr=&DataW[0];
|
||||
|
||||
while (*CurStr!=0)
|
||||
{
|
||||
wchar *NextStr=CurStr,*CmtPtr=NULL;
|
||||
while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
|
||||
{
|
||||
if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
|
||||
{
|
||||
*NextStr=0;
|
||||
CmtPtr=NextStr;
|
||||
}
|
||||
NextStr++;
|
||||
}
|
||||
bool Done=*NextStr==0;
|
||||
|
||||
*NextStr=0;
|
||||
for (wchar *SpacePtr=(CmtPtr!=NULL ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
|
||||
{
|
||||
if (*SpacePtr!=' ' && *SpacePtr!='\t')
|
||||
break;
|
||||
*SpacePtr=0;
|
||||
}
|
||||
|
||||
if (Unquote && *CurStr=='\"')
|
||||
{
|
||||
size_t Length=wcslen(CurStr);
|
||||
if (CurStr[Length-1]=='\"')
|
||||
{
|
||||
CurStr[Length-1]=0;
|
||||
CurStr++;
|
||||
}
|
||||
}
|
||||
|
||||
bool Expanded=false;
|
||||
#if defined(_WIN_ALL)
|
||||
if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
|
||||
{
|
||||
wchar ExpName[NM];
|
||||
*ExpName=0;
|
||||
DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
|
||||
Expanded=Result!=0 && Result<ASIZE(ExpName);
|
||||
if (Expanded && *ExpName!=0)
|
||||
List->AddString(ExpName);
|
||||
}
|
||||
#endif
|
||||
if (!Expanded && *CurStr!=0)
|
||||
List->AddString(CurStr);
|
||||
|
||||
if (Done)
|
||||
break;
|
||||
CurStr=NextStr+1;
|
||||
while (*CurStr=='\r' || *CurStr=='\n')
|
||||
CurStr++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef _RAR_FILESTR_
|
||||
#define _RAR_FILESTR_
|
||||
|
||||
bool ReadTextFile(
|
||||
const wchar *Name,
|
||||
StringList *List,
|
||||
bool Config,
|
||||
bool AbortOnError=false,
|
||||
RAR_CHARSET SrcCharset=RCH_DEFAULT,
|
||||
bool Unquote=false,
|
||||
bool SkipComments=false,
|
||||
bool ExpandEnvStr=false
|
||||
);
|
||||
|
||||
#endif
|
||||
218
unrar/find.cpp
218
unrar/find.cpp
@@ -1,218 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
FindFile::FindFile()
|
||||
{
|
||||
*FindMask=0;
|
||||
FirstCall=true;
|
||||
#ifdef _WIN_ALL
|
||||
hFind=INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
dirp=NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
FindFile::~FindFile()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
if (hFind!=INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
#else
|
||||
if (dirp!=NULL)
|
||||
closedir(dirp);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void FindFile::SetMask(const wchar *Mask)
|
||||
{
|
||||
wcscpy(FindMask,Mask);
|
||||
FirstCall=true;
|
||||
}
|
||||
|
||||
|
||||
bool FindFile::Next(FindData *fd,bool GetSymLink)
|
||||
{
|
||||
fd->Error=false;
|
||||
if (*FindMask==0)
|
||||
return false;
|
||||
#ifdef _WIN_ALL
|
||||
if (FirstCall)
|
||||
{
|
||||
if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
#else
|
||||
if (FirstCall)
|
||||
{
|
||||
wchar DirName[NM];
|
||||
wcsncpyz(DirName,FindMask,ASIZE(DirName));
|
||||
RemoveNameFromPath(DirName);
|
||||
if (*DirName==0)
|
||||
wcscpy(DirName,L".");
|
||||
char DirNameA[NM];
|
||||
WideToChar(DirName,DirNameA,ASIZE(DirNameA));
|
||||
if ((dirp=opendir(DirNameA))==NULL)
|
||||
{
|
||||
fd->Error=(errno!=ENOENT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
struct dirent *ent=readdir(dirp);
|
||||
if (ent==NULL)
|
||||
return false;
|
||||
if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
|
||||
continue;
|
||||
wchar Name[NM];
|
||||
if (!CharToWide(ent->d_name,Name,ASIZE(Name)))
|
||||
uiMsg(UIERROR_INVALIDNAME,UINULL,Name);
|
||||
|
||||
if (CmpName(FindMask,Name,MATCH_NAMES))
|
||||
{
|
||||
wchar FullName[NM];
|
||||
wcscpy(FullName,FindMask);
|
||||
*PointToName(FullName)=0;
|
||||
if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
|
||||
{
|
||||
uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
|
||||
return false;
|
||||
}
|
||||
wcscat(FullName,Name);
|
||||
if (!FastFind(FullName,fd,GetSymLink))
|
||||
{
|
||||
ErrHandler.OpenErrorMsg(FullName);
|
||||
continue;
|
||||
}
|
||||
wcscpy(fd->Name,FullName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fd->Flags=0;
|
||||
fd->IsDir=IsDir(fd->FileAttr);
|
||||
fd->IsLink=IsLink(fd->FileAttr);
|
||||
|
||||
FirstCall=false;
|
||||
wchar *NameOnly=PointToName(fd->Name);
|
||||
if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0)
|
||||
return Next(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
|
||||
{
|
||||
fd->Error=false;
|
||||
#ifndef _UNIX
|
||||
if (IsWildcard(FindMask))
|
||||
return false;
|
||||
#endif
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd);
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
FindClose(hFind);
|
||||
#else
|
||||
char FindMaskA[NM];
|
||||
WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
|
||||
|
||||
struct stat st;
|
||||
if (GetSymLink)
|
||||
{
|
||||
#ifdef SAVE_LINKS
|
||||
if (lstat(FindMaskA,&st)!=0)
|
||||
#else
|
||||
if (stat(FindMaskA,&st)!=0)
|
||||
#endif
|
||||
{
|
||||
fd->Error=(errno!=ENOENT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (stat(FindMaskA,&st)!=0)
|
||||
{
|
||||
fd->Error=(errno!=ENOENT);
|
||||
return false;
|
||||
}
|
||||
fd->FileAttr=st.st_mode;
|
||||
fd->Size=st.st_size;
|
||||
|
||||
#ifdef UNIX_TIME_NS
|
||||
fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
|
||||
fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
|
||||
fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
|
||||
#else
|
||||
fd->mtime.SetUnix(st.st_mtime);
|
||||
fd->atime.SetUnix(st.st_atime);
|
||||
fd->ctime.SetUnix(st.st_ctime);
|
||||
#endif
|
||||
|
||||
wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
|
||||
#endif
|
||||
fd->Flags=0;
|
||||
fd->IsDir=IsDir(fd->FileAttr);
|
||||
fd->IsLink=IsLink(fd->FileAttr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
|
||||
{
|
||||
WIN32_FIND_DATA FindData;
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
hFind=FindFirstFile(Mask,&FindData);
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
wchar LongMask[NM];
|
||||
if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask)))
|
||||
hFind=FindFirstFile(LongMask,&FindData);
|
||||
}
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
int SysErr=GetLastError();
|
||||
// We must not issue an error for "file not found" and "path not found",
|
||||
// because it is normal to not find anything for wildcard mask when
|
||||
// archiving. Also searching for non-existent file is normal in some
|
||||
// other modules, like WinRAR scanning for winrar_theme_description.txt
|
||||
// to check if any themes are available.
|
||||
fd->Error=SysErr!=ERROR_FILE_NOT_FOUND &&
|
||||
SysErr!=ERROR_PATH_NOT_FOUND &&
|
||||
SysErr!=ERROR_NO_MORE_FILES;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!FindNextFile(hFind,&FindData))
|
||||
{
|
||||
hFind=INVALID_HANDLE_VALUE;
|
||||
fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
if (hFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
wcsncpyz(fd->Name,Mask,ASIZE(fd->Name));
|
||||
SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name));
|
||||
fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
|
||||
fd->FileAttr=FindData.dwFileAttributes;
|
||||
fd->ftCreationTime=FindData.ftCreationTime;
|
||||
fd->ftLastAccessTime=FindData.ftLastAccessTime;
|
||||
fd->ftLastWriteTime=FindData.ftLastWriteTime;
|
||||
fd->mtime.SetWinFT(&FindData.ftLastWriteTime);
|
||||
fd->ctime.SetWinFT(&FindData.ftCreationTime);
|
||||
fd->atime.SetWinFT(&FindData.ftLastAccessTime);
|
||||
|
||||
|
||||
}
|
||||
fd->Flags=0;
|
||||
return hFind;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef _RAR_FINDDATA_
|
||||
#define _RAR_FINDDATA_
|
||||
|
||||
enum FINDDATA_FLAGS {
|
||||
FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode.
|
||||
};
|
||||
|
||||
struct FindData
|
||||
{
|
||||
wchar Name[NM];
|
||||
uint64 Size;
|
||||
uint FileAttr;
|
||||
bool IsDir;
|
||||
bool IsLink;
|
||||
RarTime mtime;
|
||||
RarTime ctime;
|
||||
RarTime atime;
|
||||
#ifdef _WIN_ALL
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
#endif
|
||||
uint Flags;
|
||||
bool Error;
|
||||
};
|
||||
|
||||
class FindFile
|
||||
{
|
||||
private:
|
||||
#ifdef _WIN_ALL
|
||||
static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd);
|
||||
#endif
|
||||
|
||||
wchar FindMask[NM];
|
||||
bool FirstCall;
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE hFind;
|
||||
#else
|
||||
DIR *dirp;
|
||||
#endif
|
||||
public:
|
||||
FindFile();
|
||||
~FindFile();
|
||||
void SetMask(const wchar *Mask);
|
||||
bool Next(FindData *fd,bool GetSymLink=false);
|
||||
static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
BitInput::BitInput(bool AllocBuffer)
|
||||
{
|
||||
ExternalBuffer=false;
|
||||
if (AllocBuffer)
|
||||
{
|
||||
// getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
|
||||
// So let's allocate 3 additional bytes for situation, when we need to
|
||||
// read only 1 byte from the last position of buffer and avoid a crash
|
||||
// from access to next 3 bytes, which contents we do not need.
|
||||
size_t BufSize=MAX_SIZE+3;
|
||||
InBuf=new byte[BufSize];
|
||||
|
||||
// Ensure that we get predictable results when accessing bytes in area
|
||||
// not filled with read data.
|
||||
memset(InBuf,0,BufSize);
|
||||
}
|
||||
else
|
||||
InBuf=NULL;
|
||||
}
|
||||
|
||||
|
||||
BitInput::~BitInput()
|
||||
{
|
||||
if (!ExternalBuffer)
|
||||
delete[] InBuf;
|
||||
}
|
||||
|
||||
|
||||
void BitInput::faddbits(uint Bits)
|
||||
{
|
||||
// Function wrapped version of inline addbits to save code size.
|
||||
addbits(Bits);
|
||||
}
|
||||
|
||||
|
||||
uint BitInput::fgetbits()
|
||||
{
|
||||
// Function wrapped version of inline getbits to save code size.
|
||||
return getbits();
|
||||
}
|
||||
|
||||
|
||||
void BitInput::SetExternalBuffer(byte *Buf)
|
||||
{
|
||||
if (InBuf!=NULL && !ExternalBuffer)
|
||||
delete[] InBuf;
|
||||
InBuf=Buf;
|
||||
ExternalBuffer=true;
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#ifndef _RAR_GETBITS_
|
||||
#define _RAR_GETBITS_
|
||||
|
||||
class BitInput
|
||||
{
|
||||
public:
|
||||
enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer.
|
||||
|
||||
int InAddr; // Curent byte position in the buffer.
|
||||
int InBit; // Current bit position in the current byte.
|
||||
|
||||
bool ExternalBuffer;
|
||||
public:
|
||||
BitInput(bool AllocBuffer);
|
||||
~BitInput();
|
||||
|
||||
byte *InBuf; // Dynamically allocated input buffer.
|
||||
|
||||
void InitBitInput()
|
||||
{
|
||||
InAddr=InBit=0;
|
||||
}
|
||||
|
||||
// Move forward by 'Bits' bits.
|
||||
void addbits(uint Bits)
|
||||
{
|
||||
Bits+=InBit;
|
||||
InAddr+=Bits>>3;
|
||||
InBit=Bits&7;
|
||||
}
|
||||
|
||||
// Return 16 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint getbits()
|
||||
{
|
||||
uint BitField=(uint)InBuf[InAddr] << 16;
|
||||
BitField|=(uint)InBuf[InAddr+1] << 8;
|
||||
BitField|=(uint)InBuf[InAddr+2];
|
||||
BitField >>= (8-InBit);
|
||||
return BitField & 0xffff;
|
||||
}
|
||||
|
||||
// Return 32 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint getbits32()
|
||||
{
|
||||
uint BitField=(uint)InBuf[InAddr] << 24;
|
||||
BitField|=(uint)InBuf[InAddr+1] << 16;
|
||||
BitField|=(uint)InBuf[InAddr+2] << 8;
|
||||
BitField|=(uint)InBuf[InAddr+3];
|
||||
BitField <<= InBit;
|
||||
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
|
||||
return BitField & 0xffffffff;
|
||||
}
|
||||
|
||||
void faddbits(uint Bits);
|
||||
uint fgetbits();
|
||||
|
||||
// Check if buffer has enough space for IncPtr bytes. Returns 'true'
|
||||
// if buffer will be overflown.
|
||||
bool Overflow(uint IncPtr)
|
||||
{
|
||||
return InAddr+IncPtr>=MAX_SIZE;
|
||||
}
|
||||
|
||||
void SetExternalBuffer(byte *Buf);
|
||||
};
|
||||
#endif
|
||||
@@ -1,7 +0,0 @@
|
||||
#define INCLUDEGLOBAL
|
||||
|
||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#include "rar.hpp"
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef _RAR_GLOBAL_
|
||||
#define _RAR_GLOBAL_
|
||||
|
||||
#ifdef INCLUDEGLOBAL
|
||||
#define EXTVAR
|
||||
#else
|
||||
#define EXTVAR extern
|
||||
#endif
|
||||
|
||||
EXTVAR ErrorHandler ErrHandler;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
|
||||
{
|
||||
SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
|
||||
|
||||
if (!FileExist(NameExisting))
|
||||
return false;
|
||||
CreatePath(NameNew,true);
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0;
|
||||
if (!Success)
|
||||
{
|
||||
uiMsg(UIERROR_HLINKCREATE,NameNew);
|
||||
ErrHandler.SysErrMsg();
|
||||
ErrHandler.SetErrorCode(RARX_CREATE);
|
||||
}
|
||||
return Success;
|
||||
#elif defined(_UNIX)
|
||||
char NameExistingA[NM],NameNewA[NM];
|
||||
WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA));
|
||||
WideToChar(NameNew,NameNewA,ASIZE(NameNewA));
|
||||
bool Success=link(NameExistingA,NameNewA)==0;
|
||||
if (!Success)
|
||||
{
|
||||
uiMsg(UIERROR_HLINKCREATE,NameNew);
|
||||
ErrHandler.SysErrMsg();
|
||||
ErrHandler.SetErrorCode(RARX_CREATE);
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
135
unrar/hash.cpp
135
unrar/hash.cpp
@@ -1,135 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
void HashValue::Init(HASH_TYPE Type)
|
||||
{
|
||||
HashValue::Type=Type;
|
||||
|
||||
// Zero length data CRC32 is 0. It is important to set it when creating
|
||||
// headers with no following data like directories or symlinks.
|
||||
if (Type==HASH_RAR14 || Type==HASH_CRC32)
|
||||
CRC32=0;
|
||||
if (Type==HASH_BLAKE2)
|
||||
{
|
||||
// dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f
|
||||
// is BLAKE2sp hash of empty data. We init the structure to this value,
|
||||
// so if we create a file or service header with no following data like
|
||||
// "file copy" or "symlink", we set the checksum to proper value avoiding
|
||||
// additional header type or size checks when extracting.
|
||||
static byte EmptyHash[32]={
|
||||
0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43,
|
||||
0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25,
|
||||
0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1,
|
||||
0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f
|
||||
};
|
||||
memcpy(Digest,EmptyHash,sizeof(Digest));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool HashValue::operator == (const HashValue &cmp)
|
||||
{
|
||||
if (Type==HASH_NONE || cmp.Type==HASH_NONE)
|
||||
return true;
|
||||
if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 ||
|
||||
Type==HASH_CRC32 && cmp.Type==HASH_CRC32)
|
||||
return CRC32==cmp.CRC32;
|
||||
if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2)
|
||||
return memcmp(Digest,cmp.Digest,sizeof(Digest))==0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
DataHash::DataHash()
|
||||
{
|
||||
blake2ctx=NULL;
|
||||
HashType=HASH_NONE;
|
||||
#ifdef RAR_SMP
|
||||
ThPool=NULL;
|
||||
MaxThreads=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
DataHash::~DataHash()
|
||||
{
|
||||
#ifdef RAR_SMP
|
||||
DestroyThreadPool(ThPool);
|
||||
#endif
|
||||
cleandata(&CurCRC32, sizeof(CurCRC32));
|
||||
if (blake2ctx!=NULL)
|
||||
{
|
||||
cleandata(blake2ctx, sizeof(blake2sp_state));
|
||||
delete blake2ctx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
|
||||
{
|
||||
if (blake2ctx==NULL)
|
||||
blake2ctx=new blake2sp_state;
|
||||
HashType=Type;
|
||||
if (Type==HASH_RAR14)
|
||||
CurCRC32=0;
|
||||
if (Type==HASH_CRC32)
|
||||
CurCRC32=0xffffffff; // Initial CRC32 value.
|
||||
if (Type==HASH_BLAKE2)
|
||||
blake2sp_init(blake2ctx);
|
||||
#ifdef RAR_SMP
|
||||
DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void DataHash::Update(const void *Data,size_t DataSize)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
if (HashType==HASH_RAR14)
|
||||
CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
|
||||
#endif
|
||||
if (HashType==HASH_CRC32)
|
||||
CurCRC32=CRC32(CurCRC32,Data,DataSize);
|
||||
|
||||
if (HashType==HASH_BLAKE2)
|
||||
{
|
||||
#ifdef RAR_SMP
|
||||
if (MaxThreads>1 && ThPool==NULL)
|
||||
ThPool=CreateThreadPool();
|
||||
blake2ctx->ThPool=ThPool;
|
||||
blake2ctx->MaxThreads=MaxThreads;
|
||||
#endif
|
||||
blake2sp_update( blake2ctx, (byte *)Data, DataSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DataHash::Result(HashValue *Result)
|
||||
{
|
||||
Result->Type=HashType;
|
||||
if (HashType==HASH_RAR14)
|
||||
Result->CRC32=CurCRC32;
|
||||
if (HashType==HASH_CRC32)
|
||||
Result->CRC32=CurCRC32^0xffffffff;
|
||||
if (HashType==HASH_BLAKE2)
|
||||
{
|
||||
// Preserve the original context, so we can continue hashing if necessary.
|
||||
blake2sp_state res=*blake2ctx;
|
||||
blake2sp_final(&res,Result->Digest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint DataHash::GetCRC32()
|
||||
{
|
||||
return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0;
|
||||
}
|
||||
|
||||
|
||||
bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
|
||||
{
|
||||
HashValue Final;
|
||||
Result(&Final);
|
||||
if (Key!=NULL)
|
||||
ConvertHashToMAC(&Final,Key);
|
||||
return Final==*CmpValue;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef _RAR_DATAHASH_
|
||||
#define _RAR_DATAHASH_
|
||||
|
||||
enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
|
||||
|
||||
struct HashValue
|
||||
{
|
||||
void Init(HASH_TYPE Type);
|
||||
bool operator == (const HashValue &cmp);
|
||||
bool operator != (const HashValue &cmp) {return !(*this==cmp);}
|
||||
|
||||
HASH_TYPE Type;
|
||||
union
|
||||
{
|
||||
uint CRC32;
|
||||
byte Digest[SHA256_DIGEST_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#ifdef RAR_SMP
|
||||
class ThreadPool;
|
||||
class DataHash;
|
||||
#endif
|
||||
|
||||
|
||||
class DataHash
|
||||
{
|
||||
private:
|
||||
HASH_TYPE HashType;
|
||||
uint CurCRC32;
|
||||
blake2sp_state *blake2ctx;
|
||||
|
||||
#ifdef RAR_SMP
|
||||
ThreadPool *ThPool;
|
||||
|
||||
uint MaxThreads;
|
||||
// Upper limit for maximum threads to prevent wasting threads in pool.
|
||||
static const uint MaxHashThreads=8;
|
||||
#endif
|
||||
public:
|
||||
DataHash();
|
||||
~DataHash();
|
||||
void Init(HASH_TYPE Type,uint MaxThreads);
|
||||
void Update(const void *Data,size_t DataSize);
|
||||
void Result(HashValue *Result);
|
||||
uint GetCRC32();
|
||||
bool Cmp(HashValue *CmpValue,byte *Key);
|
||||
HASH_TYPE Type() {return HashType;}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
void FileHeader::Reset(size_t SubDataSize)
|
||||
{
|
||||
SubData.Alloc(SubDataSize);
|
||||
BaseBlock::Reset();
|
||||
FileHash.Init(HASH_NONE);
|
||||
mtime.Reset();
|
||||
atime.Reset();
|
||||
ctime.Reset();
|
||||
SplitBefore=false;
|
||||
SplitAfter=false;
|
||||
|
||||
UnknownUnpSize=0;
|
||||
|
||||
SubFlags=0; // Important for RAR 3.0 subhead.
|
||||
|
||||
CryptMethod=CRYPT_NONE;
|
||||
Encrypted=false;
|
||||
SaltSet=false;
|
||||
UsePswCheck=false;
|
||||
UseHashKey=false;
|
||||
Lg2Count=0;
|
||||
|
||||
Solid=false;
|
||||
Dir=false;
|
||||
WinSize=0;
|
||||
Inherited=false;
|
||||
SubBlock=false;
|
||||
CommentInHeader=false;
|
||||
Version=false;
|
||||
LargeFile=false;
|
||||
|
||||
RedirType=FSREDIR_NONE;
|
||||
DirTarget=false;
|
||||
UnixOwnerSet=false;
|
||||
}
|
||||
|
||||
|
||||
FileHeader& FileHeader::operator = (FileHeader &hd)
|
||||
{
|
||||
SubData.Reset();
|
||||
memcpy(this,&hd,sizeof(*this));
|
||||
SubData.CleanData();
|
||||
SubData=hd.SubData;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void MainHeader::Reset()
|
||||
{
|
||||
HighPosAV=0;
|
||||
PosAV=0;
|
||||
CommentInHeader=false;
|
||||
PackComment=false;
|
||||
Locator=false;
|
||||
QOpenOffset=0;
|
||||
QOpenMaxSize=0;
|
||||
RROffset=0;
|
||||
RRMaxSize=0;
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
#ifndef _RAR_HEADERS_
|
||||
#define _RAR_HEADERS_
|
||||
|
||||
#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header.
|
||||
#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header.
|
||||
#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
|
||||
#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
|
||||
#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
|
||||
#define SIZEOF_SHORTBLOCKHEAD 7
|
||||
#define SIZEOF_LONGBLOCKHEAD 11
|
||||
#define SIZEOF_SUBBLOCKHEAD 14
|
||||
#define SIZEOF_COMMHEAD 13
|
||||
#define SIZEOF_PROTECTHEAD 26
|
||||
#define SIZEOF_AVHEAD 14
|
||||
#define SIZEOF_SIGNHEAD 15
|
||||
#define SIZEOF_UOHEAD 18
|
||||
#define SIZEOF_MACHEAD 22
|
||||
#define SIZEOF_EAHEAD 24
|
||||
#define SIZEOF_BEEAHEAD 24
|
||||
#define SIZEOF_STREAMHEAD 26
|
||||
|
||||
#define VER_PACK 29
|
||||
#define VER_PACK5 50 // It is stored as 0, but we subtract 50 when saving an archive.
|
||||
#define VER_UNPACK 29
|
||||
#define VER_UNPACK5 50 // It is stored as 0, but we add 50 when reading an archive.
|
||||
|
||||
#define MHD_VOLUME 0x0001U
|
||||
|
||||
// Old style main archive comment embed into main archive header. Must not
|
||||
// be used in new archives anymore.
|
||||
#define MHD_COMMENT 0x0002U
|
||||
|
||||
#define MHD_LOCK 0x0004U
|
||||
#define MHD_SOLID 0x0008U
|
||||
#define MHD_PACK_COMMENT 0x0010U
|
||||
#define MHD_NEWNUMBERING 0x0010U
|
||||
#define MHD_AV 0x0020U
|
||||
#define MHD_PROTECT 0x0040U
|
||||
#define MHD_PASSWORD 0x0080U
|
||||
#define MHD_FIRSTVOLUME 0x0100U
|
||||
|
||||
#define LHD_SPLIT_BEFORE 0x0001U
|
||||
#define LHD_SPLIT_AFTER 0x0002U
|
||||
#define LHD_PASSWORD 0x0004U
|
||||
|
||||
// Old style file comment embed into file header. Must not be used
|
||||
// in new archives anymore.
|
||||
#define LHD_COMMENT 0x0008U
|
||||
|
||||
// For non-file subheaders it denotes 'subblock having a parent file' flag.
|
||||
#define LHD_SOLID 0x0010U
|
||||
|
||||
|
||||
#define LHD_WINDOWMASK 0x00e0U
|
||||
#define LHD_WINDOW64 0x0000U
|
||||
#define LHD_WINDOW128 0x0020U
|
||||
#define LHD_WINDOW256 0x0040U
|
||||
#define LHD_WINDOW512 0x0060U
|
||||
#define LHD_WINDOW1024 0x0080U
|
||||
#define LHD_WINDOW2048 0x00a0U
|
||||
#define LHD_WINDOW4096 0x00c0U
|
||||
#define LHD_DIRECTORY 0x00e0U
|
||||
|
||||
#define LHD_LARGE 0x0100U
|
||||
#define LHD_UNICODE 0x0200U
|
||||
#define LHD_SALT 0x0400U
|
||||
#define LHD_VERSION 0x0800U
|
||||
#define LHD_EXTTIME 0x1000U
|
||||
|
||||
#define SKIP_IF_UNKNOWN 0x4000U
|
||||
#define LONG_BLOCK 0x8000U
|
||||
|
||||
#define EARC_NEXT_VOLUME 0x0001U // Not last volume.
|
||||
#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes).
|
||||
#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record.
|
||||
#define EARC_VOLNUMBER 0x0008U // Store a number of current volume.
|
||||
|
||||
enum HEADER_TYPE {
|
||||
// RAR 5.0 header types.
|
||||
HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03,
|
||||
HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff,
|
||||
|
||||
// RAR 1.5 - 4.x header types.
|
||||
HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75,
|
||||
HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79,
|
||||
HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b
|
||||
};
|
||||
|
||||
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
|
||||
NTACL_HEAD=0x104,STREAM_HEAD=0x105 };
|
||||
|
||||
|
||||
// Internal implementation, depends on archive format version.
|
||||
enum HOST_SYSTEM {
|
||||
// RAR 5.0 host OS
|
||||
HOST5_WINDOWS=0,HOST5_UNIX=1,
|
||||
|
||||
// RAR 3.0 host OS.
|
||||
HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
|
||||
HOST_BEOS=5,HOST_MAX
|
||||
};
|
||||
|
||||
// Unified archive format independent implementation.
|
||||
enum HOST_SYSTEM_TYPE {
|
||||
HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
// We also use these values in extra field, so do not modify them.
|
||||
enum FILE_SYSTEM_REDIRECT {
|
||||
FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
|
||||
FSREDIR_HARDLINK, FSREDIR_FILECOPY
|
||||
};
|
||||
|
||||
|
||||
#define SUBHEAD_TYPE_CMT L"CMT"
|
||||
#define SUBHEAD_TYPE_QOPEN L"QO"
|
||||
#define SUBHEAD_TYPE_ACL L"ACL"
|
||||
#define SUBHEAD_TYPE_STREAM L"STM"
|
||||
#define SUBHEAD_TYPE_UOWNER L"UOW"
|
||||
#define SUBHEAD_TYPE_AV L"AV"
|
||||
#define SUBHEAD_TYPE_RR L"RR"
|
||||
#define SUBHEAD_TYPE_OS2EA L"EA2"
|
||||
|
||||
/* new file inherits a subblock when updating a host file */
|
||||
#define SUBHEAD_FLAGS_INHERITED 0x80000000
|
||||
|
||||
#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001
|
||||
|
||||
|
||||
struct MarkHeader
|
||||
{
|
||||
byte Mark[8];
|
||||
|
||||
// Following fields are virtual and not present in real blocks.
|
||||
uint HeadSize;
|
||||
};
|
||||
|
||||
|
||||
struct BaseBlock
|
||||
{
|
||||
uint HeadCRC; // 'ushort' for RAR 1.5.
|
||||
HEADER_TYPE HeaderType; // 1 byte for RAR 1.5.
|
||||
uint Flags; // 'ushort' for RAR 1.5.
|
||||
uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0.
|
||||
|
||||
bool SkipIfUnknown;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
SkipIfUnknown=false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BlockHeader:BaseBlock
|
||||
{
|
||||
uint DataSize;
|
||||
};
|
||||
|
||||
|
||||
struct MainHeader:BaseBlock
|
||||
{
|
||||
ushort HighPosAV;
|
||||
uint PosAV;
|
||||
bool CommentInHeader;
|
||||
bool PackComment; // For RAR 1.4 archive format only.
|
||||
bool Locator;
|
||||
uint64 QOpenOffset; // Offset of quick list record.
|
||||
uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
|
||||
uint64 RROffset; // Offset of recovery record.
|
||||
uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
|
||||
void Reset();
|
||||
};
|
||||
|
||||
|
||||
struct FileHeader:BlockHeader
|
||||
{
|
||||
byte HostOS;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
union {
|
||||
uint FileAttr;
|
||||
uint SubFlags;
|
||||
};
|
||||
wchar FileName[NM];
|
||||
|
||||
Array<byte> SubData;
|
||||
|
||||
RarTime mtime;
|
||||
RarTime ctime;
|
||||
RarTime atime;
|
||||
|
||||
int64 PackSize;
|
||||
int64 UnpSize;
|
||||
int64 MaxSize; // Reserve size bytes for vint of this size.
|
||||
|
||||
HashValue FileHash;
|
||||
|
||||
uint FileFlags;
|
||||
|
||||
bool SplitBefore;
|
||||
bool SplitAfter;
|
||||
|
||||
bool UnknownUnpSize;
|
||||
|
||||
bool Encrypted;
|
||||
CRYPT_METHOD CryptMethod;
|
||||
bool SaltSet;
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte InitV[SIZE_INITV];
|
||||
bool UsePswCheck;
|
||||
byte PswCheck[SIZE_PSWCHECK];
|
||||
|
||||
// Use HMAC calculated from HashKey and checksum instead of plain checksum.
|
||||
bool UseHashKey;
|
||||
|
||||
// Key to convert checksum to HMAC. Derived from password with PBKDF2
|
||||
// using additional iterations.
|
||||
byte HashKey[SHA256_DIGEST_SIZE];
|
||||
|
||||
uint Lg2Count; // Log2 of PBKDF2 repetition count.
|
||||
|
||||
bool Solid;
|
||||
bool Dir;
|
||||
bool CommentInHeader; // RAR 2.0 file comment.
|
||||
bool Version; // name.ext;ver file name containing the version number.
|
||||
size_t WinSize;
|
||||
bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).
|
||||
|
||||
// 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
|
||||
bool LargeFile;
|
||||
|
||||
// 'true' for HEAD_SERVICE block, which is a child of preceding file block.
|
||||
// RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives.
|
||||
bool SubBlock;
|
||||
|
||||
HOST_SYSTEM_TYPE HSType;
|
||||
|
||||
FILE_SYSTEM_REDIRECT RedirType;
|
||||
wchar RedirName[NM];
|
||||
bool DirTarget;
|
||||
|
||||
bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
|
||||
char UnixOwnerName[256],UnixGroupName[256];
|
||||
#ifdef _UNIX
|
||||
uid_t UnixOwnerID;
|
||||
gid_t UnixGroupID;
|
||||
#else // Need these Unix fields in Windows too for 'list' command.
|
||||
uint UnixOwnerID;
|
||||
uint UnixGroupID;
|
||||
#endif
|
||||
|
||||
void Reset(size_t SubDataSize=0);
|
||||
|
||||
bool CmpName(const wchar *Name)
|
||||
{
|
||||
return(wcscmp(FileName,Name)==0);
|
||||
}
|
||||
|
||||
FileHeader& operator = (FileHeader &hd);
|
||||
};
|
||||
|
||||
|
||||
struct EndArcHeader:BaseBlock
|
||||
{
|
||||
// Optional CRC32 of entire archive up to start of EndArcHeader block.
|
||||
// Present in RAR 4.x archives if EARC_DATACRC flag is set.
|
||||
uint ArcDataCRC;
|
||||
|
||||
uint VolNumber; // Optional number of current volume.
|
||||
|
||||
// 7 additional zero bytes can be stored here if EARC_REVSPACE is set.
|
||||
|
||||
bool NextVolume; // Not last volume.
|
||||
bool DataCRC;
|
||||
bool RevSpace;
|
||||
bool StoreVolNumber;
|
||||
void Reset()
|
||||
{
|
||||
BaseBlock::Reset();
|
||||
NextVolume=false;
|
||||
DataCRC=false;
|
||||
RevSpace=false;
|
||||
StoreVolNumber=false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CryptHeader:BaseBlock
|
||||
{
|
||||
bool UsePswCheck;
|
||||
uint Lg2Count; // Log2 of PBKDF2 repetition count.
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte PswCheck[SIZE_PSWCHECK];
|
||||
};
|
||||
|
||||
|
||||
// SubBlockHeader and its successors were used in RAR 2.x format.
|
||||
// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks.
|
||||
struct SubBlockHeader:BlockHeader
|
||||
{
|
||||
ushort SubType;
|
||||
byte Level;
|
||||
};
|
||||
|
||||
|
||||
struct CommentHeader:BaseBlock
|
||||
{
|
||||
ushort UnpSize;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
ushort CommCRC;
|
||||
};
|
||||
|
||||
|
||||
struct ProtectHeader:BlockHeader
|
||||
{
|
||||
byte Version;
|
||||
ushort RecSectors;
|
||||
uint TotalBlocks;
|
||||
byte Mark[8];
|
||||
};
|
||||
|
||||
|
||||
struct AVHeader:BaseBlock
|
||||
{
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
byte AVVer;
|
||||
uint AVInfoCRC;
|
||||
};
|
||||
|
||||
|
||||
struct SignHeader:BaseBlock
|
||||
{
|
||||
uint CreationTime;
|
||||
ushort ArcNameSize;
|
||||
ushort UserNameSize;
|
||||
};
|
||||
|
||||
|
||||
struct UnixOwnersHeader:SubBlockHeader
|
||||
{
|
||||
ushort OwnerNameSize;
|
||||
ushort GroupNameSize;
|
||||
/* dummy */
|
||||
char OwnerName[256];
|
||||
char GroupName[256];
|
||||
};
|
||||
|
||||
|
||||
struct EAHeader:SubBlockHeader
|
||||
{
|
||||
uint UnpSize;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
uint EACRC;
|
||||
};
|
||||
|
||||
|
||||
struct StreamHeader:SubBlockHeader
|
||||
{
|
||||
uint UnpSize;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
uint StreamCRC;
|
||||
ushort StreamNameSize;
|
||||
char StreamName[260];
|
||||
};
|
||||
|
||||
|
||||
struct MacFInfoHeader:SubBlockHeader
|
||||
{
|
||||
uint fileType;
|
||||
uint fileCreator;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,100 +0,0 @@
|
||||
#ifndef _RAR_HEADERS5_
|
||||
#define _RAR_HEADERS5_
|
||||
|
||||
#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length.
|
||||
#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size.
|
||||
|
||||
// RAR 5.0 block flags common for all blocks.
|
||||
|
||||
// Additional extra area is present in the end of block header.
|
||||
#define HFL_EXTRA 0x0001
|
||||
// Additional data area is present in the end of block header.
|
||||
#define HFL_DATA 0x0002
|
||||
// Unknown blocks with this flag must be skipped when updating an archive.
|
||||
#define HFL_SKIPIFUNKNOWN 0x0004
|
||||
// Data area of this block is continuing from previous volume.
|
||||
#define HFL_SPLITBEFORE 0x0008
|
||||
// Data area of this block is continuing in next volume.
|
||||
#define HFL_SPLITAFTER 0x0010
|
||||
// Block depends on preceding file block.
|
||||
#define HFL_CHILD 0x0020
|
||||
// Preserve a child block if host is modified.
|
||||
#define HFL_INHERITED 0x0040
|
||||
|
||||
// RAR 5.0 main archive header specific flags.
|
||||
#define MHFL_VOLUME 0x0001 // Volume.
|
||||
#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first.
|
||||
#define MHFL_SOLID 0x0004 // Solid archive.
|
||||
#define MHFL_PROTECT 0x0008 // Recovery record is present.
|
||||
#define MHFL_LOCK 0x0010 // Locked archive.
|
||||
|
||||
// RAR 5.0 file header specific flags.
|
||||
#define FHFL_DIRECTORY 0x0001 // Directory.
|
||||
#define FHFL_UTIME 0x0002 // Time field in Unix format is present.
|
||||
#define FHFL_CRC32 0x0004 // CRC32 field is present.
|
||||
#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size.
|
||||
|
||||
// RAR 5.0 end of archive header specific flags.
|
||||
#define EHFL_NEXTVOLUME 0x0001 // Not last volume.
|
||||
|
||||
// RAR 5.0 archive encryption header specific flags.
|
||||
#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present.
|
||||
|
||||
|
||||
// RAR 5.0 file compression flags.
|
||||
#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm.
|
||||
#define FCI_ALGO_BIT1 0x0002 // 0 .. 63.
|
||||
#define FCI_ALGO_BIT2 0x0004
|
||||
#define FCI_ALGO_BIT3 0x0008
|
||||
#define FCI_ALGO_BIT4 0x0010
|
||||
#define FCI_ALGO_BIT5 0x0020
|
||||
#define FCI_SOLID 0x0040 // Solid flag.
|
||||
#define FCI_METHOD_BIT0 0x0080 // Compression method.
|
||||
#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used).
|
||||
#define FCI_METHOD_BIT2 0x0200
|
||||
#define FCI_DICT_BIT0 0x0400 // Dictionary size.
|
||||
#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB.
|
||||
#define FCI_DICT_BIT2 0x1000
|
||||
#define FCI_DICT_BIT3 0x2000
|
||||
|
||||
// Main header extra field values.
|
||||
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
|
||||
|
||||
// Flags for MHEXTRA_LOCATOR.
|
||||
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
|
||||
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
|
||||
|
||||
// File and service header extra field values.
|
||||
#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
|
||||
#define FHEXTRA_HASH 0x02 // File hash.
|
||||
#define FHEXTRA_HTIME 0x03 // High precision file time.
|
||||
#define FHEXTRA_VERSION 0x04 // File version information.
|
||||
#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.).
|
||||
#define FHEXTRA_UOWNER 0x06 // Unix owner and group information.
|
||||
#define FHEXTRA_SUBDATA 0x07 // Service header subdata array.
|
||||
|
||||
|
||||
// Hash type values for FHEXTRA_HASH.
|
||||
#define FHEXTRA_HASH_BLAKE2 0x00
|
||||
|
||||
// Flags for FHEXTRA_HTIME.
|
||||
#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format.
|
||||
#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present.
|
||||
#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present.
|
||||
#define FHEXTRA_HTIME_ATIME 0x08 // atime is present.
|
||||
#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision.
|
||||
|
||||
// Flags for FHEXTRA_CRYPT.
|
||||
#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data.
|
||||
#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums.
|
||||
|
||||
// Flags for FHEXTRA_REDIR.
|
||||
#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory.
|
||||
|
||||
// Flags for FHEXTRA_UOWNER.
|
||||
#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present.
|
||||
#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present.
|
||||
#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present.
|
||||
#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present.
|
||||
|
||||
#endif
|
||||
@@ -1,24 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
DWORD WinNT()
|
||||
{
|
||||
static int dwPlatformId=-1;
|
||||
static DWORD dwMajorVersion,dwMinorVersion;
|
||||
if (dwPlatformId==-1)
|
||||
{
|
||||
OSVERSIONINFO WinVer;
|
||||
WinVer.dwOSVersionInfoSize=sizeof(WinVer);
|
||||
GetVersionEx(&WinVer);
|
||||
dwPlatformId=WinVer.dwPlatformId;
|
||||
dwMajorVersion=WinVer.dwMajorVersion;
|
||||
dwMinorVersion=WinVer.dwMinorVersion;
|
||||
}
|
||||
DWORD Result=0;
|
||||
if (dwPlatformId==VER_PLATFORM_WIN32_NT)
|
||||
Result=dwMajorVersion*0x100+dwMinorVersion;
|
||||
|
||||
|
||||
return Result;
|
||||
}
|
||||
#endif
|
||||
@@ -1,13 +0,0 @@
|
||||
#ifndef _RAR_ISNT_
|
||||
#define _RAR_ISNT_
|
||||
|
||||
enum WINNT_VERSION {
|
||||
WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
|
||||
WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
|
||||
WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00
|
||||
};
|
||||
|
||||
DWORD WinNT();
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
****** ***** ****** UnRAR - free utility for RAR archives
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
****** ******* ****** License for use and distribution of
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
** ** ** ** ** ** FREE portable version
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The source code of UnRAR utility is freeware. This means:
|
||||
|
||||
1. All copyrights to RAR and the utility UnRAR are exclusively
|
||||
owned by the author - Alexander Roshal.
|
||||
|
||||
2. UnRAR source code may be used in any software to handle
|
||||
RAR archives without limitations free of charge, but cannot be
|
||||
used to develop RAR (WinRAR) compatible archiver and to
|
||||
re-create RAR compression algorithm, which is proprietary.
|
||||
Distribution of modified UnRAR source code in separate form
|
||||
or as a part of other software is permitted, provided that
|
||||
full text of this paragraph, starting from "UnRAR source code"
|
||||
words, is included in license, or in documentation if license
|
||||
is not available, and in source code comments of resulting package.
|
||||
|
||||
3. The UnRAR utility may be freely distributed. It is allowed
|
||||
to distribute UnRAR inside of other software packages.
|
||||
|
||||
4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
|
||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
||||
OR MISUSING THIS SOFTWARE.
|
||||
|
||||
5. Installing and using the UnRAR utility signifies acceptance of
|
||||
these terms and conditions of the license.
|
||||
|
||||
6. If you don't agree with terms of the license you must remove
|
||||
UnRAR files from your storage devices and cease to use the
|
||||
utility.
|
||||
|
||||
Thank you for your interest in RAR and UnRAR.
|
||||
|
||||
|
||||
Alexander L. Roshal
|
||||
472
unrar/list.cpp
472
unrar/list.cpp
@@ -1,472 +0,0 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare);
|
||||
static void ListSymLink(Archive &Arc);
|
||||
static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize);
|
||||
static void ListOldSubHeader(Archive &Arc);
|
||||
static void ListNewSubHeader(CommandData *Cmd,Archive &Arc);
|
||||
|
||||
void ListArchive(CommandData *Cmd)
|
||||
{
|
||||
int64 SumPackSize=0,SumUnpSize=0;
|
||||
uint ArcCount=0,SumFileCount=0;
|
||||
bool Technical=(Cmd->Command[1]=='T');
|
||||
bool ShowService=Technical && Cmd->Command[2]=='A';
|
||||
bool Bare=(Cmd->Command[1]=='B');
|
||||
bool Verbose=(Cmd->Command[0]=='V');
|
||||
|
||||
wchar ArcName[NM];
|
||||
while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
|
||||
{
|
||||
if (Cmd->ManualPassword)
|
||||
Cmd->Password.Clean(); // Clean user entered password before processing next archive.
|
||||
|
||||
Archive Arc(Cmd);
|
||||
#ifdef _WIN_ALL
|
||||
Arc.RemoveSequentialFlag();
|
||||
#endif
|
||||
if (!Arc.WOpen(ArcName))
|
||||
continue;
|
||||
bool FileMatched=true;
|
||||
while (1)
|
||||
{
|
||||
int64 TotalPackSize=0,TotalUnpSize=0;
|
||||
uint FileCount=0;
|
||||
if (Arc.IsArchive(true))
|
||||
{
|
||||
bool TitleShown=false;
|
||||
if (!Bare)
|
||||
{
|
||||
Arc.ViewComment();
|
||||
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName);
|
||||
mprintf(L"\n%s: ",St(MListDetails));
|
||||
uint SetCount=0;
|
||||
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5");
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt);
|
||||
if (Arc.Solid)
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid));
|
||||
if (Arc.SFXSize>0)
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX));
|
||||
if (Arc.Volume)
|
||||
if (Arc.Format==RARFMT50)
|
||||
{
|
||||
// RAR 5.0 archives store the volume number in main header,
|
||||
// so it is already available now.
|
||||
if (SetCount++ > 0)
|
||||
mprintf(L", ");
|
||||
mprintf(St(MVolumeNumber),Arc.VolNumber+1);
|
||||
}
|
||||
else
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume));
|
||||
if (Arc.Protected)
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR));
|
||||
if (Arc.Locked)
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock));
|
||||
if (Arc.Encrypted)
|
||||
mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead));
|
||||
mprintf(L"\n");
|
||||
}
|
||||
|
||||
wchar VolNumText[50];
|
||||
*VolNumText=0;
|
||||
while(Arc.ReadHeader()>0)
|
||||
{
|
||||
HEADER_TYPE HeaderType=Arc.GetHeaderType();
|
||||
if (HeaderType==HEAD_ENDARC)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
// Only RAR 1.5 archives store the volume number in end record.
|
||||
if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15)
|
||||
swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1);
|
||||
#endif
|
||||
if (Technical && ShowService)
|
||||
{
|
||||
mprintf(L"\n%12ls: %ls",St(MListService),L"EOF");
|
||||
if (*VolNumText!=0)
|
||||
mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText);
|
||||
mprintf(L"\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch(HeaderType)
|
||||
{
|
||||
case HEAD_FILE:
|
||||
FileMatched=Cmd->IsProcessFile(Arc.FileHead)!=0;
|
||||
if (FileMatched)
|
||||
{
|
||||
ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare);
|
||||
if (!Arc.FileHead.SplitBefore)
|
||||
{
|
||||
TotalUnpSize+=Arc.FileHead.UnpSize;
|
||||
FileCount++;
|
||||
}
|
||||
TotalPackSize+=Arc.FileHead.PackSize;
|
||||
}
|
||||
break;
|
||||
case HEAD_SERVICE:
|
||||
if (FileMatched && !Bare)
|
||||
{
|
||||
if (Technical && ShowService)
|
||||
ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Arc.SeekToNext();
|
||||
}
|
||||
if (!Bare && !Technical)
|
||||
if (TitleShown)
|
||||
{
|
||||
wchar UnpSizeText[20];
|
||||
itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText));
|
||||
|
||||
wchar PackSizeText[20];
|
||||
itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText));
|
||||
|
||||
if (Verbose)
|
||||
{
|
||||
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
|
||||
mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText,
|
||||
PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize),
|
||||
VolNumText,FileCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
mprintf(L"\n----------- --------- ---------- ----- ----");
|
||||
mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
|
||||
}
|
||||
|
||||
SumFileCount+=FileCount;
|
||||
SumUnpSize+=TotalUnpSize;
|
||||
SumPackSize+=TotalPackSize;
|
||||
mprintf(L"\n");
|
||||
}
|
||||
else
|
||||
mprintf(St(MListNoFiles));
|
||||
|
||||
ArcCount++;
|
||||
|
||||
#ifndef NOVOLUME
|
||||
if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter ||
|
||||
Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) &&
|
||||
MergeArchive(Arc,NULL,false,Cmd->Command[0]))
|
||||
Arc.Seek(0,SEEK_SET);
|
||||
else
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Cmd->ArcNames.ItemsCount()<2 && !Bare)
|
||||
mprintf(St(MNotRAR),Arc.FileName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean user entered password. Not really required, just for extra safety.
|
||||
if (Cmd->ManualPassword)
|
||||
Cmd->Password.Clean();
|
||||
|
||||
if (ArcCount>1 && !Bare && !Technical)
|
||||
{
|
||||
wchar UnpSizeText[20],PackSizeText[20];
|
||||
itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText));
|
||||
itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText));
|
||||
|
||||
if (Verbose)
|
||||
mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText,
|
||||
ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount);
|
||||
else
|
||||
mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum LISTCOL_TYPE {
|
||||
LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR
|
||||
};
|
||||
|
||||
|
||||
void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare)
|
||||
{
|
||||
wchar *Name=hd.FileName;
|
||||
RARFORMAT Format=Arc.Format;
|
||||
|
||||
if (Bare)
|
||||
{
|
||||
mprintf(L"%s\n",Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TitleShown && !Technical)
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
mprintf(L"\n%ls",St(MListTitleV));
|
||||
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
|
||||
}
|
||||
else
|
||||
{
|
||||
mprintf(L"\n%ls",St(MListTitleL));
|
||||
mprintf(L"\n----------- --------- ---------- ----- ----");
|
||||
}
|
||||
TitleShown=true;
|
||||
}
|
||||
|
||||
wchar UnpSizeText[30],PackSizeText[30];
|
||||
if (hd.UnpSize==INT64NDF)
|
||||
wcscpy(UnpSizeText,L"?");
|
||||
else
|
||||
itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText));
|
||||
itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText));
|
||||
|
||||
wchar AttrStr[30];
|
||||
if (hd.HeaderType==HEAD_SERVICE)
|
||||
swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.');
|
||||
else
|
||||
ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr));
|
||||
|
||||
wchar RatioStr[10];
|
||||
|
||||
if (hd.SplitBefore && hd.SplitAfter)
|
||||
wcscpy(RatioStr,L"<->");
|
||||
else
|
||||
if (hd.SplitBefore)
|
||||
wcscpy(RatioStr,L"<--");
|
||||
else
|
||||
if (hd.SplitAfter)
|
||||
wcscpy(RatioStr,L"-->");
|
||||
else
|
||||
swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
|
||||
|
||||
wchar DateStr[50];
|
||||
hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical);
|
||||
|
||||
if (Technical)
|
||||
{
|
||||
mprintf(L"\n%12s: %s",St(MListName),Name);
|
||||
|
||||
bool FileBlock=hd.HeaderType==HEAD_FILE;
|
||||
|
||||
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
|
||||
{
|
||||
mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream));
|
||||
wchar StreamName[NM];
|
||||
GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName));
|
||||
mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName);
|
||||
}
|
||||
else
|
||||
{
|
||||
const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService);
|
||||
|
||||
if (hd.RedirType!=FSREDIR_NONE)
|
||||
switch(hd.RedirType)
|
||||
{
|
||||
case FSREDIR_UNIXSYMLINK:
|
||||
Type=St(MListUSymlink); break;
|
||||
case FSREDIR_WINSYMLINK:
|
||||
Type=St(MListWSymlink); break;
|
||||
case FSREDIR_JUNCTION:
|
||||
Type=St(MListJunction); break;
|
||||
case FSREDIR_HARDLINK:
|
||||
Type=St(MListHardlink); break;
|
||||
case FSREDIR_FILECOPY:
|
||||
Type=St(MListCopy); break;
|
||||
}
|
||||
mprintf(L"\n%12ls: %ls",St(MListType),Type);
|
||||
if (hd.RedirType!=FSREDIR_NONE)
|
||||
if (Format==RARFMT15)
|
||||
{
|
||||
char LinkTargetA[NM];
|
||||
if (Arc.FileHead.Encrypted)
|
||||
{
|
||||
// Link data are encrypted. We would need to ask for password
|
||||
// and initialize decryption routine to display the link target.
|
||||
strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA));
|
||||
}
|
||||
else
|
||||
{
|
||||
int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1);
|
||||
Arc.Read(LinkTargetA,DataSize);
|
||||
LinkTargetA[DataSize > 0 ? DataSize : 0] = 0;
|
||||
}
|
||||
wchar LinkTarget[NM];
|
||||
CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget));
|
||||
mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget);
|
||||
}
|
||||
else
|
||||
mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName);
|
||||
}
|
||||
if (!hd.Dir)
|
||||
{
|
||||
mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText);
|
||||
mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText);
|
||||
mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr);
|
||||
}
|
||||
if (hd.mtime.IsSet())
|
||||
mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr);
|
||||
if (hd.ctime.IsSet())
|
||||
{
|
||||
hd.ctime.GetText(DateStr,ASIZE(DateStr),true);
|
||||
mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr);
|
||||
}
|
||||
if (hd.atime.IsSet())
|
||||
{
|
||||
hd.atime.GetText(DateStr,ASIZE(DateStr),true);
|
||||
mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr);
|
||||
}
|
||||
mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr);
|
||||
if (hd.FileHash.Type==HASH_CRC32)
|
||||
mprintf(L"\n%12ls: %8.8X",
|
||||
hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32",
|
||||
hd.FileHash.CRC32);
|
||||
if (hd.FileHash.Type==HASH_BLAKE2)
|
||||
{
|
||||
wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1];
|
||||
BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr));
|
||||
mprintf(L"\n%12ls: %ls",
|
||||
hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2",
|
||||
BlakeStr);
|
||||
}
|
||||
|
||||
const wchar *HostOS=L"";
|
||||
if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN)
|
||||
HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix";
|
||||
if (Format==RARFMT15)
|
||||
{
|
||||
static const wchar *RarOS[]={
|
||||
L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L""
|
||||
};
|
||||
if (hd.HostOS<ASIZE(RarOS))
|
||||
HostOS=RarOS[hd.HostOS];
|
||||
}
|
||||
if (*HostOS!=0)
|
||||
mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS);
|
||||
|
||||
mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo),
|
||||
Format==RARFMT15 ? L"3.0":L"5.0",hd.UnpVer,hd.Method,
|
||||
hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400,
|
||||
hd.WinSize>=0x100000 ? L"M":L"K");
|
||||
|
||||
if (hd.Solid || hd.Encrypted)
|
||||
{
|
||||
mprintf(L"\n%12ls: ",St(MListFlags));
|
||||
if (hd.Solid)
|
||||
mprintf(L"%ls ",St(MListSolid));
|
||||
if (hd.Encrypted)
|
||||
mprintf(L"%ls ",St(MListEnc));
|
||||
}
|
||||
|
||||
if (hd.Version)
|
||||
{
|
||||
uint Version=ParseVersionFileName(Name,false);
|
||||
if (Version!=0)
|
||||
mprintf(L"\n%12ls: %u",St(MListFileVer),Version);
|
||||
}
|
||||
|
||||
if (hd.UnixOwnerSet)
|
||||
{
|
||||
mprintf(L"\n%12ls: ",L"Unix owner");
|
||||
if (*hd.UnixOwnerName!=0)
|
||||
mprintf(L"%ls:",GetWide(hd.UnixOwnerName));
|
||||
if (*hd.UnixGroupName!=0)
|
||||
mprintf(L"%ls",GetWide(hd.UnixGroupName));
|
||||
if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric))
|
||||
mprintf(L" ");
|
||||
if (hd.UnixOwnerNumeric)
|
||||
mprintf(L"#%d:",hd.UnixOwnerID);
|
||||
if (hd.UnixGroupNumeric)
|
||||
mprintf(L"#%d:",hd.UnixGroupID);
|
||||
}
|
||||
|
||||
mprintf(L"\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
|
||||
|
||||
if (Verbose)
|
||||
mprintf(L"%9ls %4ls ",PackSizeText,RatioStr);
|
||||
|
||||
mprintf(L" %ls ",DateStr);
|
||||
|
||||
if (Verbose)
|
||||
{
|
||||
if (hd.FileHash.Type==HASH_CRC32)
|
||||
mprintf(L"%8.8X ",hd.FileHash.CRC32);
|
||||
else
|
||||
if (hd.FileHash.Type==HASH_BLAKE2)
|
||||
{
|
||||
byte *S=hd.FileHash.Digest;
|
||||
mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]);
|
||||
}
|
||||
else
|
||||
mprintf(L"???????? ");
|
||||
}
|
||||
mprintf(L"%ls",Name);
|
||||
}
|
||||
|
||||
/*
|
||||
void ListSymLink(Archive &Arc)
|
||||
{
|
||||
if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000)
|
||||
if (Arc.FileHead.Encrypted)
|
||||
{
|
||||
// Link data are encrypted. We would need to ask for password
|
||||
// and initialize decryption routine to display the link target.
|
||||
mprintf(L"\n%22ls %ls",L"-->",L"*<-?->");
|
||||
}
|
||||
else
|
||||
{
|
||||
char FileName[NM];
|
||||
uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1);
|
||||
Arc.Read(FileName,DataSize);
|
||||
FileName[DataSize]=0;
|
||||
mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize)
|
||||
{
|
||||
switch(HostType)
|
||||
{
|
||||
case HSYS_WINDOWS:
|
||||
swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c",
|
||||
(A & 0x2000)!=0 ? 'I' : '.', // Not content indexed.
|
||||
(A & 0x0800)!=0 ? 'C' : '.', // Compressed.
|
||||
(A & 0x0020)!=0 ? 'A' : '.', // Archive.
|
||||
(A & 0x0010)!=0 ? 'D' : '.', // Directory.
|
||||
(A & 0x0004)!=0 ? 'S' : '.', // System.
|
||||
(A & 0x0002)!=0 ? 'H' : '.', // Hidden.
|
||||
(A & 0x0001)!=0 ? 'R' : '.'); // Read-only.
|
||||
break;
|
||||
case HSYS_UNIX:
|
||||
switch (A & 0xF000)
|
||||
{
|
||||
case 0x4000:
|
||||
AttrStr[0]='d';
|
||||
break;
|
||||
case 0xA000:
|
||||
AttrStr[0]='l';
|
||||
break;
|
||||
default:
|
||||
AttrStr[0]='-';
|
||||
break;
|
||||
}
|
||||
swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c",
|
||||
(A & 0x0100) ? 'r' : '-',
|
||||
(A & 0x0080) ? 'w' : '-',
|
||||
(A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'),
|
||||
(A & 0x0020) ? 'r' : '-',
|
||||
(A & 0x0010) ? 'w' : '-',
|
||||
(A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'),
|
||||
(A & 0x0004) ? 'r' : '-',
|
||||
(A & 0x0002) ? 'w' : '-',
|
||||
(A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-');
|
||||
break;
|
||||
case HSYS_UNKNOWN:
|
||||
wcscpy(AttrStr,L"?");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user