1
0
Fork 0

feat: initial commit

This commit is contained in:
Thomas Touhey 2024-01-18 01:35:29 +01:00
commit 2203a58a08
115 changed files with 18132 additions and 0 deletions

97
.clang-format Normal file
View File

@ -0,0 +1,97 @@
---
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
AlignConsecutiveAssignments: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: Consecutive
AlignEscapedNewlines: DontAlign
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: true
AttributeMacros: ['CAHUTE_WUR', 'CAHUTE_DEPRECATED', 'CAHUTE_EXTERN']
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: After
BraceWrapping:
AfterCaseLabel: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeElse: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BracedInitializerIndentWidth: 4
BreakAfterAttributes: Never
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakStringLiterals: true
ColumnLimit: 79
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
IncludeBlocks: Preserve
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: NoIndent
IndentGotoLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: true
InsertTrailingCommas: None
KeepEmptyLinesAtEOF: false
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
Language: Cpp
LineEnding: LF
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
PPIndentWidth: 1
PointerAlignment: Right
QualifierAlignment: Custom
QualifierOrder: ['constexpr', 'static', 'inline', 'type', 'volatile', 'const', 'restrict']
ReferenceAlignment: Right
ReflowComments: false
RemoveParentheses: Leave
RemoveSemicolon: true
SortIncludes: Never
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAroundPointerQualifiers: Both
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInSquareBrackets: false
Standard: Latest
TabWidth: 4
UseTab: Never

358
.gitignore vendored Normal file
View File

@ -0,0 +1,358 @@
# Created by https://www.toptal.com/developers/gitignore/api/linux,osx,macos,sublimetext,kate,kdevelop4,cmake,c,python
# Edit at https://www.toptal.com/developers/gitignore?templates=linux,osx,macos,sublimetext,kate,kdevelop4,cmake,c,python
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### CMake ###
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
### CMake Patch ###
# External projects
*-prefix/
### Kate ###
# Swap Files #
.*.kate-swp
.swp.*
### KDevelop4 ###
*.kdev4
.kdev4/
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### OSX ###
# General
# Icon must end with two \r
# Thumbnails
# Files that might appear in the root of a volume
# Directories potentially created on remote AFP share
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
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
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__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/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
### SublimeText ###
# Cache files for Sublime Text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# Workspace files are user-specific
*.sublime-workspace
# Project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using Sublime Text
# *.sublime-project
# SFTP configuration file
sftp-config.json
sftp-config-alt*.json
# Package control specific files
Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
Package Control.merged-ca-bundle
Package Control.user-ca-bundle
oscrypto-ca-bundle.crt
bh_unicode_properties.cache
# Sublime-github package stores a github token in this file
# https://packagecontrol.io/packages/sublime-github
GitHub.sublime-settings
# End of https://www.toptal.com/developers/gitignore/api/linux,osx,macos,sublimetext,kate,kdevelop4,cmake,c,python
/include/cahute/config.h
/CPack*
/_CPack*
/*.sh
/*.tar.*
/p7*
/p7screen*
/xfer9860*
/.envrc
/vgcore.*
/misc/cahute.pc

73
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,73 @@
stages:
- lint
- build
- publish
pre-commit:
stage: lint
image: python:3.11
before_script:
- pip install pre-commit==3.6.0
script:
- pre-commit run -v --all-files
build (linux):
stage: build
image: debian:bookworm-slim
before_script:
- apt-get update -y
- apt-get install -y cmake pkg-config gcc libusb-1.0-0-dev libsdl2-dev
script:
- cmake -B build -S . -DCMAKE_INSTALL_PREFIX=/usr
- cmake --build build
build docs:
stage: build
image: python:3.11
before_script:
- pip install -r docs/requirements.txt
script:
- sphinx-build -M html docs docs/_build
artifacts:
paths:
- docs/_build/html
publish docs:
stage: publish
rules:
- if: $CI_COMMIT_TAG
when: always
image: debian:bookworm-slim
before_script:
- apt-get update -y
- apt-get install -y rsync openssh-client
- eval $(ssh-agent -s)
- mkdir ~/.ssh && chmod 700 ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >~/.ssh/config
- chmod 400 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
script:
- rsync -Prlt --delete docs/_build/html/ gitlab-cahute@cahuteproject.org:/var/www/cahuteproject/www
dependencies:
- build docs
publish tarball:
stage: publish
rules:
- if: $CI_COMMIT_TAG
when: always
image: debian:bookworm-slim
before_script:
- apt-get update -y
- apt-get install -y cmake pkg-config gcc libusb-1.0-0-dev libsdl2-dev openssh-client
- eval $(ssh-agent -s)
- mkdir ~/.ssh && chmod 700 ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >~/.ssh/config
- chmod 400 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
script:
- cmake -B build -S .
- cpack --config build/CPackSourceConfig.cmake
- export RELEASE_FILE=$(ls cahute-*.tar.gz)
- sha256sum $RELEASE_FILE >$RELEASE_FILE.sha256
- scp $RELEASE_FILE $RELEASE_FILE.sha256 gitlab-cahute@cahuteproject.org:/var/www/cahuteproject/ftp/releases

21
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,21 @@
repos:
- repo: https://github.com/commitizen-tools/commitizen
rev: v2.38.0
hooks:
- id: commitizen
stages: [commit-msg]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-ast
- id: check-json
- id: mixed-line-ending
- id: end-of-file-fixer
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v17.0.6
hooks:
- id: clang-format

1
.tool-versions Normal file
View File

@ -0,0 +1 @@
python 3.11.5

128
CMakeLists.txt Normal file
View File

@ -0,0 +1,128 @@
cmake_minimum_required(VERSION 3.16)
project(
cahute
VERSION 0.1
DESCRIPTION "Communication and file format handling tools for CASIO calculators"
HOMEPAGE_URL "https://cahuteproject.org/"
LANGUAGES C
)
set(PROJECT_ISSUES_URL "https://cahuteproject.org/guides/report.html")
add_custom_target(lint COMMAND pre-commit run --all-files)
include(GNUInstallDirs)
find_package(PkgConfig REQUIRED)
find_package(SDL2 REQUIRED)
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_SOURCE_GENERATOR "TGZ")
set(
CPACK_SOURCE_IGNORE_FILES
"\\.git" "build/" ".*~$" "\\.envrc" "\\.pre-commit-config\\.yaml"
"\\.clang-format" "__pycache__" "\\.gitlab-ci.yml" "_CPack"
)
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_VERSION}")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
set(CPACK_VERBATIM_VARIABLES true)
include(CPack)
set(LOGLEVEL $ENV{LOGLEVEL})
if(NOT "${LOGLEVEL}" MATCHES "|none|info|warning|error|fatal")
message(SEND_ERROR "LOGLEVEL should be one of [info, warning, error, fatal, none]")
endif()
if(NOT LOGLEVEL)
set(LOGLEVEL "fatal")
endif()
string(TOUPPER ${LOGLEVEL} DEFAULT_LOGLEVEL)
execute_process(
COMMAND printf "0x%02X%02X0000" ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR}
OUTPUT_VARIABLE PROJECT_VERSION_HEX
)
configure_file(include/cahute/config.h.in include/cahute/config.h ESCAPE_QUOTES)
configure_file(misc/cahute.pc.in misc/cahute.pc ESCAPE_QUOTES @ONLY)
# ---
# Build options.
# ---
pkg_check_modules(libusb REQUIRED libusb-1.0 IMPORTED_TARGET)
set(CMAKE_C_STANDARD 90)
add_compile_options(
-Wall -Wextra -Wno-unused-macros -Wno-vla
-Wshadow -Wwrite-strings -Wredundant-decls -Wformat -Wformat-nonliteral
-Wformat-security -Wimplicit-function-declaration
-Wdate-time -Wmissing-prototypes -Wreturn-type -Wpointer-arith
-Wstack-protector -Wno-unused-parameter
)
add_library(${PROJECT_NAME} STATIC
lib/cdefs.c
lib/detection.c
lib/link.c
lib/logging.c
lib/misc.c
lib/seven.c
lib/seven_ohp.c
lib/stream.c
)
add_executable(p7
cli/p7.c
cli/p7_args.c
cli/common.c
)
add_executable(p7screen
cli/p7screen.c
cli/p7screen_args.c
cli/common.c
)
add_executable(xfer9860
cli/xfer9860.c
cli/xfer9860_args.c
cli/common.c
)
include_directories(
"${CMAKE_CURRENT_BINARY_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${libusb_INCLUDE_DIRS}"
)
target_include_directories(p7screen PRIVATE ${SDL2_INCLUDE_DIR})
target_link_libraries(p7 PRIVATE ${PROJECT_NAME} PkgConfig::libusb)
target_link_libraries(p7screen PRIVATE ${PROJECT_NAME} PkgConfig::libusb ${SDL2_LIBRARIES})
target_link_libraries(xfer9860 PRIVATE ${PROJECT_NAME} PkgConfig::libusb)
# ---
# Packaging options.
# ---
install(TARGETS ${PROJECT_NAME} ARCHIVE)
install(TARGETS p7 RUNTIME)
install(TARGETS p7screen RUNTIME)
install(TARGETS xfer9860 RUNTIME)
install(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" "${CMAKE_CURRENT_BINARY_DIR}/include/"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
FILES_MATCHING PATTERN "*.h"
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/misc/cahute.pc
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
install(
FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/60-casio-calculators.rules
DESTINATION "${CMAKE_INSTALL_LIBDIR}/udev/rules.d"
)

518
LICENSE.txt Normal file
View File

@ -0,0 +1,518 @@
CeCILL FREE SOFTWARE LICENSE AGREEMENT
Version 2.1 dated 2013-06-21
Notice
This Agreement is a Free Software license agreement that is the result
of discussions between its authors in order to ensure compliance with
the two main principles guiding its drafting:
* firstly, compliance with the principles governing the distribution
of Free Software: access to source code, broad rights granted to users,
* secondly, the election of a governing law, French law, with which it
is conformant, both as regards the law of torts and intellectual
property law, and the protection that it offers to both authors and
holders of the economic rights over software.
The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
license are:
Commissariat à l'énergie atomique et aux énergies alternatives - CEA, a
public scientific, technical and industrial research establishment,
having its principal place of business at 25 rue Leblanc, immeuble Le
Ponant D, 75015 Paris, France.
Centre National de la Recherche Scientifique - CNRS, a public scientific
and technological establishment, having its principal place of business
at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
Institut National de Recherche en Informatique et en Automatique -
Inria, a public scientific and technological establishment, having its
principal place of business at Domaine de Voluceau, Rocquencourt, BP
105, 78153 Le Chesnay cedex, France.
Preamble
The purpose of this Free Software license agreement is to grant users
the right to modify and redistribute the software governed by this
license within the framework of an open source distribution model.
The exercising of this right is conditional upon certain obligations for
users so as to preserve this status for all subsequent redistributions.
In consideration of access to the source code and the rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors only have limited liability.
In this respect, the risks associated with loading, using, modifying
and/or developing or reproducing the software by the user are brought to
the user's attention, given its Free Software status, which may make it
complicated to use, with the result that its use is reserved for
developers and experienced professionals having in-depth computer
knowledge. Users are therefore encouraged to load and test the
suitability of the software as regards their requirements in conditions
enabling the security of their systems and/or data to be ensured and,
more generally, to use and operate it in the same conditions of
security. This Agreement may be freely reproduced and published,
provided it is not altered, and that no provisions are either added or
removed herefrom.
This Agreement may apply to any or all software for which the holder of
the economic rights decides to submit the use thereof to its provisions.
Frequently asked questions can be found on the official website of the
CeCILL licenses family (http://www.cecill.info/index.en.html) for any
necessary clarification.
Article 1 - DEFINITIONS
For the purpose of this Agreement, when the following expressions
commence with a capital letter, they shall have the following meaning:
Agreement: means this license agreement, and its possible subsequent
versions and annexes.
Software: means the software in its Object Code and/or Source Code form
and, where applicable, its documentation, "as is" when the Licensee
accepts the Agreement.
Initial Software: means the Software in its Source Code and possibly its
Object Code form and, where applicable, its documentation, "as is" when
it is first distributed under the terms and conditions of the Agreement.
Modified Software: means the Software modified by at least one
Contribution.
Source Code: means all the Software's instructions and program lines to
which access is required so as to modify the Software.
Object Code: means the binary files originating from the compilation of
the Source Code.
Holder: means the holder(s) of the economic rights over the Initial
Software.
Licensee: means the Software user(s) having accepted the Agreement.
Contributor: means a Licensee having made at least one Contribution.
Licensor: means the Holder, or any other individual or legal entity, who
distributes the Software under the Agreement.
Contribution: means any or all modifications, corrections, translations,
adaptations and/or new functions integrated into the Software by any or
all Contributors, as well as any or all Internal Modules.
Module: means a set of sources files including their documentation that
enables supplementary functions or services in addition to those offered
by the Software.
External Module: means any or all Modules, not derived from the
Software, so that this Module and the Software run in separate address
spaces, with one calling the other when they are run.
Internal Module: means any or all Module, connected to the Software so
that they both execute in the same address space.
GNU GPL: means the GNU General Public License version 2 or any
subsequent version, as published by the Free Software Foundation Inc.
GNU Affero GPL: means the GNU Affero General Public License version 3 or
any subsequent version, as published by the Free Software Foundation Inc.
EUPL: means the European Union Public License version 1.1 or any
subsequent version, as published by the European Commission.
Parties: mean both the Licensee and the Licensor.
These expressions may be used both in singular and plural form.
Article 2 - PURPOSE
The purpose of the Agreement is the grant by the Licensor to the
Licensee of a non-exclusive, transferable and worldwide license for the
Software as set forth in Article 5 <#scope> hereinafter for the whole
term of the protection granted by the rights over said Software.
Article 3 - ACCEPTANCE
3.1 The Licensee shall be deemed as having accepted the terms and
conditions of this Agreement upon the occurrence of the first of the
following events:
* (i) loading the Software by any or all means, notably, by
downloading from a remote server, or by loading from a physical medium;
* (ii) the first time the Licensee exercises any of the rights granted
hereunder.
3.2 One copy of the Agreement, containing a notice relating to the
characteristics of the Software, to the limited warranty, and to the
fact that its use is restricted to experienced users has been provided
to the Licensee prior to its acceptance as set forth in Article 3.1
<#accepting> hereinabove, and the Licensee hereby acknowledges that it
has read and understood it.
Article 4 - EFFECTIVE DATE AND TERM
4.1 EFFECTIVE DATE
The Agreement shall become effective on the date when it is accepted by
the Licensee as set forth in Article 3.1 <#accepting>.
4.2 TERM
The Agreement shall remain in force for the entire legal term of
protection of the economic rights over the Software.
Article 5 - SCOPE OF RIGHTS GRANTED
The Licensor hereby grants to the Licensee, who accepts, the following
rights over the Software for any or all use, and for the term of the
Agreement, on the basis of the terms and conditions set forth hereinafter.
Besides, if the Licensor owns or comes to own one or more patents
protecting all or part of the functions of the Software or of its
components, the Licensor undertakes not to enforce the rights granted by
these patents against successive Licensees using, exploiting or
modifying the Software. If these patents are transferred, the Licensor
undertakes to have the transferees subscribe to the obligations set
forth in this paragraph.
5.1 RIGHT OF USE
The Licensee is authorized to use the Software, without any limitation
as to its fields of application, with it being hereinafter specified
that this comprises:
1. permanent or temporary reproduction of all or part of the Software
by any or all means and in any or all form.
2. loading, displaying, running, or storing the Software on any or all
medium.
3. entitlement to observe, study or test its operation so as to
determine the ideas and principles behind any or all constituent
elements of said Software. This shall apply when the Licensee
carries out any or all loading, displaying, running, transmission or
storage operation as regards the Software, that it is entitled to
carry out hereunder.
5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
The right to make Contributions includes the right to translate, adapt,
arrange, or make any or all modifications to the Software, and the right
to reproduce the resulting software.
The Licensee is authorized to make any or all Contributions to the
Software provided that it includes an explicit notice that it is the
author of said Contribution and indicates the date of the creation thereof.
5.3 RIGHT OF DISTRIBUTION
In particular, the right of distribution includes the right to publish,
transmit and communicate the Software to the general public on any or
all medium, and by any or all means, and the right to market, either in
consideration of a fee, or free of charge, one or more copies of the
Software by any means.
The Licensee is further authorized to distribute copies of the modified
or unmodified Software to third parties according to the terms and
conditions set forth hereinafter.
5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
The Licensee is authorized to distribute true copies of the Software in
Source Code or Object Code form, provided that said distribution
complies with all the provisions of the Agreement and is accompanied by:
1. a copy of the Agreement,
2. a notice relating to the limitation of both the Licensor's warranty
and liability as set forth in Articles 8 and 9,
and that, in the event that only the Object Code of the Software is
redistributed, the Licensee allows effective access to the full Source
Code of the Software for a period of at least three years from the
distribution of the Software, it being understood that the additional
acquisition cost of the Source Code shall not exceed the cost of the
data transfer.
5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
When the Licensee makes a Contribution to the Software, the terms and
conditions for the distribution of the resulting Modified Software
become subject to all the provisions of this Agreement.
The Licensee is authorized to distribute the Modified Software, in
source code or object code form, provided that said distribution
complies with all the provisions of the Agreement and is accompanied by:
1. a copy of the Agreement,
2. a notice relating to the limitation of both the Licensor's warranty
and liability as set forth in Articles 8 and 9,
and, in the event that only the object code of the Modified Software is
redistributed,
3. a note stating the conditions of effective access to the full source
code of the Modified Software for a period of at least three years
from the distribution of the Modified Software, it being understood
that the additional acquisition cost of the source code shall not
exceed the cost of the data transfer.
5.3.3 DISTRIBUTION OF EXTERNAL MODULES
When the Licensee has developed an External Module, the terms and
conditions of this Agreement do not apply to said External Module, that
may be distributed under a separate license agreement.
5.3.4 COMPATIBILITY WITH OTHER LICENSES
The Licensee can include a code that is subject to the provisions of one
of the versions of the GNU GPL, GNU Affero GPL and/or EUPL in the
Modified or unmodified Software, and distribute that entire code under
the terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
The Licensee can include the Modified or unmodified Software in a code
that is subject to the provisions of one of the versions of the GNU GPL,
GNU Affero GPL and/or EUPL and distribute that entire code under the
terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
Article 6 - INTELLECTUAL PROPERTY
6.1 OVER THE INITIAL SOFTWARE
The Holder owns the economic rights over the Initial Software. Any or
all use of the Initial Software is subject to compliance with the terms
and conditions under which the Holder has elected to distribute its work
and no one shall be entitled to modify the terms and conditions for the
distribution of said Initial Software.
The Holder undertakes that the Initial Software will remain ruled at
least by this Agreement, for the duration set forth in Article 4.2 <#term>.
6.2 OVER THE CONTRIBUTIONS
The Licensee who develops a Contribution is the owner of the
intellectual property rights over this Contribution as defined by
applicable law.
6.3 OVER THE EXTERNAL MODULES
The Licensee who develops an External Module is the owner of the
intellectual property rights over this External Module as defined by
applicable law and is free to choose the type of agreement that shall
govern its distribution.
6.4 JOINT PROVISIONS
The Licensee expressly undertakes:
1. not to remove, or modify, in any manner, the intellectual property
notices attached to the Software;
2. to reproduce said notices, in an identical manner, in the copies of
the Software modified or not.
The Licensee undertakes not to directly or indirectly infringe the
intellectual property rights on the Software of the Holder and/or
Contributors, and to take, where applicable, vis-à-vis its staff, any
and all measures required to ensure respect of said intellectual
property rights of the Holder and/or Contributors.
Article 7 - RELATED SERVICES
7.1 Under no circumstances shall the Agreement oblige the Licensor to
provide technical assistance or maintenance services for the Software.
However, the Licensor is entitled to offer this type of services. The
terms and conditions of such technical assistance, and/or such
maintenance, shall be set forth in a separate instrument. Only the
Licensor offering said maintenance and/or technical assistance services
shall incur liability therefor.
7.2 Similarly, any Licensor is entitled to offer to its licensees, under
its sole responsibility, a warranty, that shall only be binding upon
itself, for the redistribution of the Software and/or the Modified
Software, under terms and conditions that it is free to decide. Said
warranty, and the financial terms and conditions of its application,
shall be subject of a separate instrument executed between the Licensor
and the Licensee.
Article 8 - LIABILITY
8.1 Subject to the provisions of Article 8.2, the Licensee shall be
entitled to claim compensation for any direct loss it may have suffered
from the Software as a result of a fault on the part of the relevant
Licensor, subject to providing evidence thereof.
8.2 The Licensor's liability is limited to the commitments made under
this Agreement and shall not be incurred as a result of in particular:
(i) loss due the Licensee's total or partial failure to fulfill its
obligations, (ii) direct or consequential loss that is suffered by the
Licensee due to the use or performance of the Software, and (iii) more
generally, any consequential loss. In particular the Parties expressly
agree that any or all pecuniary or business loss (i.e. loss of data,
loss of profits, operating loss, loss of customers or orders,
opportunity cost, any disturbance to business activities) or any or all
legal proceedings instituted against the Licensee by a third party,
shall constitute consequential loss and shall not provide entitlement to
any or all compensation from the Licensor.
Article 9 - WARRANTY
9.1 The Licensee acknowledges that the scientific and technical
state-of-the-art when the Software was distributed did not enable all
possible uses to be tested and verified, nor for the presence of
possible defects to be detected. In this respect, the Licensee's
attention has been drawn to the risks associated with loading, using,
modifying and/or developing and reproducing the Software which are
reserved for experienced users.
The Licensee shall be responsible for verifying, by any or all means,
the suitability of the product for its requirements, its good working
order, and for ensuring that it shall not cause damage to either persons
or properties.
9.2 The Licensor hereby represents, in good faith, that it is entitled
to grant all the rights over the Software (including in particular the
rights set forth in Article 5 <#scope>).
9.3 The Licensee acknowledges that the Software is supplied "as is" by
the Licensor without any other express or tacit warranty, other than
that provided for in Article 9.2 <#good-faith> and, in particular,
without any warranty as to its commercial value, its secured, safe,
innovative or relevant nature.
Specifically, the Licensor does not warrant that the Software is free
from any error, that it will operate without interruption, that it will
be compatible with the Licensee's own equipment and software
configuration, nor that it will meet the Licensee's requirements.
9.4 The Licensor does not either expressly or tacitly warrant that the
Software does not infringe any third party intellectual property right
relating to a patent, software or any other property right. Therefore,
the Licensor disclaims any and all liability towards the Licensee
arising out of any or all proceedings for infringement that may be
instituted in respect of the use, modification and redistribution of the
Software. Nevertheless, should such proceedings be instituted against
the Licensee, the Licensor shall provide it with technical and legal
expertise for its defense. Such technical and legal expertise shall be
decided on a case-by-case basis between the relevant Licensor and the
Licensee pursuant to a memorandum of understanding. The Licensor
disclaims any and all liability as regards the Licensee's use of the
name of the Software. No warranty is given as regards the existence of
prior rights over the name of the Software or as regards the existence
of a trademark.
Article 10 - TERMINATION
10.1 In the event of a breach by the Licensee of its obligations
hereunder, the Licensor may automatically terminate this Agreement
thirty (30) days after notice has been sent to the Licensee and has
remained ineffective.
10.2 A Licensee whose Agreement is terminated shall no longer be
authorized to use, modify or distribute the Software. However, any
licenses that it may have granted prior to termination of the Agreement
shall remain valid subject to their having been granted in compliance
with the terms and conditions hereof.
Article 11 - MISCELLANEOUS
11.1 EXCUSABLE EVENTS
Neither Party shall be liable for any or all delay, or failure to
perform the Agreement, that may be attributable to an event of force
majeure, an act of God or an outside cause, such as defective
functioning or interruptions of the electricity or telecommunications
networks, network paralysis following a virus attack, intervention by
government authorities, natural disasters, water damage, earthquakes,
fire, explosions, strikes and labor unrest, war, etc.
11.2 Any failure by either Party, on one or more occasions, to invoke
one or more of the provisions hereof, shall under no circumstances be
interpreted as being a waiver by the interested Party of its right to
invoke said provision(s) subsequently.
11.3 The Agreement cancels and replaces any or all previous agreements,
whether written or oral, between the Parties and having the same
purpose, and constitutes the entirety of the agreement between said
Parties concerning said purpose. No supplement or modification to the
terms and conditions hereof shall be effective as between the Parties
unless it is made in writing and signed by their duly authorized
representatives.
11.4 In the event that one or more of the provisions hereof were to
conflict with a current or future applicable act or legislative text,
said act or legislative text shall prevail, and the Parties shall make
the necessary amendments so as to comply with said act or legislative
text. All other provisions shall remain effective. Similarly, invalidity
of a provision of the Agreement, for any reason whatsoever, shall not
cause the Agreement as a whole to be invalid.
11.5 LANGUAGE
The Agreement is drafted in both French and English and both versions
are deemed authentic.
Article 12 - NEW VERSIONS OF THE AGREEMENT
12.1 Any person is authorized to duplicate and distribute copies of this
Agreement.
12.2 So as to ensure coherence, the wording of this Agreement is
protected and may only be modified by the authors of the License, who
reserve the right to periodically publish updates or new versions of the
Agreement, each with a separate number. These subsequent versions may
address new issues encountered by Free Software.
12.3 Any Software distributed under a given version of the Agreement may
only be subsequently distributed under the same version of the Agreement
or a subsequent version, subject to the provisions of Article 5.3.4
<#compatibility>.
Article 13 - GOVERNING LAW AND JURISDICTION
13.1 The Agreement is governed by French law. The Parties agree to
endeavor to seek an amicable solution to any disagreements or disputes
that may arise during the performance of the Agreement.
13.2 Failing an amicable solution within two (2) months as from their
occurrence, and unless emergency proceedings are necessary, the
disagreements or disputes shall be referred to the Paris Courts having
jurisdiction, by the more diligent Party.

19
README.rst Normal file
View File

@ -0,0 +1,19 @@
Cahute -- Communication and file format handling tools for CASIO calculators
============================================================================
Cahute is a library and set of command-line utilities to handle serial
and USB communication protocols and file formats related to CASIO calculators,
dating from the 1990s to today.
For more information, consult the following links:
* `Cahute documentation`_, for more information on the project and its context;
* `Issue tracker`_, and the `issue reporting guide`_;
* `Pending contributions`_, and the related `contributing guide`_.
.. _Cahute documentation: https://cahuteproject.org/
.. _Issue tracker: https://gitlab.com/cahuteproject/cahute/-/issues
.. _Issue reporting guide: https://cahuteproject.org/guides/report.html
.. _Pending contributions:
https://gitlab.com/cahuteproject/cahute/-/merge_requests
.. _Contributing guide: https://cahuteproject.org/guides/contribute.html

174
cli/common.c Normal file
View File

@ -0,0 +1,174 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "common.h"
#include <stdio.h>
#include <string.h>
/**
* Cookie for the USB finder.
*
* @property should_display Whether the program should display the options
* if multiple are found.
* @property multiple Boolean set to 1 if multiple devices were already
* detected.
* @property last_bus Bus of the last found device, -1 if uninitialized.
* @property last_address Address of the last found device, -1 if
* uninitialized.
* @property last_type Type of the last found device, -1 if uninitialized.
*/
struct usb_device_finder_cookie {
int should_display;
int multiple;
int last_bus;
int last_address;
int last_type;
};
/**
* USB finder callback.
*
* @param cookie Cookie of the USB device finder.
*/
static int find_usb_device(
struct usb_device_finder_cookie *cookie,
cahute_usb_detection_entry const *entry
) {
if (cookie->last_bus >= 0) {
if (!cookie->multiple && cookie->should_display) {
fprintf(stderr, "Multiple USB calculators were found:\n");
fprintf(
stderr,
" Bus %03d Device %03d: %s\n",
cookie->last_bus,
cookie->last_address,
cookie->last_type == CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN
? "fx-9860G compatible calculator (Protocol 7.00)"
: "fx-CG compatible calculator (SCSI)"
);
}
cookie->multiple = 1;
if (cookie->should_display) {
fprintf(
stderr,
" Bus %03d Device %03d: %s\n",
entry->cahute_usb_detection_entry_bus,
entry->cahute_usb_detection_entry_address,
entry->cahute_usb_detection_entry_type
== CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN
? "fx-9860G compatible calculator (Protocol 7.00)"
: "fx-CG compatible calculator (SCSI)"
);
}
}
cookie->last_bus = entry->cahute_usb_detection_entry_bus;
cookie->last_address = entry->cahute_usb_detection_entry_address;
cookie->last_type = entry->cahute_usb_detection_entry_type;
return 0;
}
/**
* Find out the USB bus and address of the only USB device.
*
* @param should_display Whether the program should display the options, if
* multiple are present.
* @param busp Pointer to the bus to define.
* @param addressp Pointer to the address to define.
* @return Cahute error (> 0), -1 if multiple devices were found, or 0 if ok.
*/
extern int find_usb_calculator(int should_display, int *busp, int *addressp) {
struct usb_device_finder_cookie cookie;
int err;
cookie.should_display = should_display;
cookie.last_bus = -1;
cookie.last_address = -1;
cookie.last_type = -1;
cookie.multiple = 0;
err = cahute_detect_usb(
(cahute_detect_usb_entry_func *)find_usb_device,
&cookie
);
if (err)
return err;
if (cookie.last_bus < 0)
return CAHUTE_ERROR_NOT_FOUND;
if (cookie.multiple)
return CAHUTE_ERROR_TOO_MANY;
*busp = cookie.last_bus;
*addressp = cookie.last_address;
return 0;
}
/**
* Get the current logging level as a string.
*
* @return Logging level name.
*/
extern char const *get_current_log_level(void) {
int loglevel = cahute_get_log_level();
switch (loglevel) {
case CAHUTE_LOGLEVEL_INFO:
return "info";
case CAHUTE_LOGLEVEL_WARNING:
return "warning";
case CAHUTE_LOGLEVEL_ERROR:
return "error";
case CAHUTE_LOGLEVEL_FATAL:
return "fatal";
default:
return "(none)";
}
}
/**
* Set the current logging level as a string.
*
* @param loglevel Name of the loglevel to set.
*/
extern void set_log_level(char const *loglevel) {
int value = CAHUTE_LOGLEVEL_NONE;
if (!strcmp(loglevel, "info"))
value = CAHUTE_LOGLEVEL_INFO;
else if (!strcmp(loglevel, "warning"))
value = CAHUTE_LOGLEVEL_WARNING;
else if (!strcmp(loglevel, "error"))
value = CAHUTE_LOGLEVEL_ERROR;
else if (!strcmp(loglevel, "fatal"))
value = CAHUTE_LOGLEVEL_FATAL;
cahute_set_log_level(value);
}

38
cli/common.h Normal file
View File

@ -0,0 +1,38 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef COMMON_H
#define COMMON_H 1
#include <cahute.h>
extern int find_usb_calculator(int should_display, int *busp, int *addressp);
extern char const *get_current_log_level(void);
extern void set_log_level(char const *loglevel);
#endif /* COMMON_H */

509
cli/p7.c Normal file
View File

@ -0,0 +1,509 @@
/* ****************************************************************************
* Copyright (C) 2016-2017, 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "p7.h"
#include <stdlib.h>
#include <string.h>
/* The operation was not implemented (yet). */
static char const error_notimplemented[] =
"The requested operation was not implemented yet.\n";
/* Couldn't initialize connexion with the calculator. */
static char const error_notfound[] =
"Could not connect to the calculator.\n"
"- Is it plugged in and in receive mode?\n"
"- Have you tried changing the cable?\n";
/* Calculator was disconnected. */
static char const error_disconnected[] =
"Lost connexion to the calculator!\n"
"Please reconnect the calculator, rerun receive mode and try again.\n";
/* Calculator was found but program wasn't allowed to communicate with it. */
static char const error_noaccess[] =
"Could not get access to the calculator.\n"
"Install the appropriate udev rule, or run as root.\n";
/* Command was unsupported. */
static char const error_unsupported[] =
"The command is unsupported by the calculator.\n"
"- Does the calculator have mass storage?\n"
"- Does its OS allow the use of it?\n"
"- Is it in Receive Mode (and not in OS Update)?\n";
/* The calculator acted in an unplanned way. */
static char const error_unplanned[] =
"The calculator didn't act as planned.\n"
"Stop receive mode on calculator and start it again before "
"re-running %s.\n";
/**
* Print a serial device.
*
* @param firstp Whether the first entry has already been printed or not,
* as a cookie.
* @param entry Entry to display.
*/
static int
print_serial_device(int *firstp, cahute_serial_detection_entry const *entry) {
if (!*firstp) {
printf("Available devices:\n\n");
*firstp = 1;
}
printf("- %s\n", entry->cahute_serial_detection_entry_name);
return 0;
}
/**
* Print a storage entry for file and directory listing.
*
* @param cookie (unused)
* @param entry Entry.
* @return 0, so that the file listing goes to the end.
*/
static int
print_storage_entry(void *cookie, cahute_storage_entry const *entry) {
char const *directory = entry->cahute_storage_entry_directory;
char const *name = entry->cahute_storage_entry_name;
char formatted_name[30];
(void)cookie;
snprintf(
formatted_name,
28,
"%s%s%s",
directory ? directory : "",
directory ? "/" : "",
name ? name : ""
);
printf(
"%-27.27s %10luo\n",
formatted_name,
entry->cahute_storage_entry_size
);
return 0;
}
/**
* Display progress.
*
* @param initp Pointer to an integer (as a cookie) to set to 1 if the
* function has been called.
* @param step Index of the latest accomplished step.
* @param total Total number of steps to accomplish.
*/
static void
display_progress(int *initp, unsigned long step, unsigned long total) {
char buf[50];
unsigned long i, percent = 10000 * step / total;
*initp = 1;
sprintf(
buf,
"\r|---------------------------------------| %02lu.%02lu%%",
(percent / 100) % 100,
percent % 100
);
for (i = 39 * step / total; i--;)
buf[2 + i] = '#';
fputs(buf, stdout);
fflush(stdout);
}
/**
* Request user confirmation for an overwrite interactively.
*
* @param cookie (unused)
* @return 1 if the overwrite is confirmed, 0 otherwise.
*/
static int confirm_overwrite(void *cookie) {
char line[12];
(void)cookie;
printf("It looks like the file already exists on the calculator.\n");
printf("Overwrite? ([n]/y) ");
if (!fgets(line, 10, stdin))
return 0;
return line[0] == 'y' || line[0] == 'Y';
}
/**
* Open a link depending on the parsed command-line.
*
* This function also takes care of changing the serial attributes, if the
* opened link is on a serial medium.
*
* @param linkp Pointer to the link to initialize.
* @param args Parsed parameters to base ourselves on.
* @return Cahute error, or CAHUTE_OK if everything is ok.
*/
static int open_link(cahute_link **linkp, struct args const *args) {
cahute_link *link = NULL;
int bus, address, err;
unsigned long flags;
if (args->serial_name) {
/* The user has selected a serial stream! */
flags = args->serial_flags | CAHUTE_SERIAL_PROTOCOL_SEVEN;
if (args->no_init)
flags |= CAHUTE_SERIAL_NOCHECK;
if (args->no_term)
flags |= CAHUTE_SERIAL_NOTERM;
err = cahute_open_serial_link(
&link,
flags,
args->serial_name,
args->serial_speed
);
if (err)
return err;
if (args->change_serial) {
/* We want to change the serial settings as part of the
* opening procedure. If this fails, we need to actually close
* the link. */
err = cahute_negotiate_serial_params(
link,
args->new_serial_flags,
args->new_serial_speed
);
if (err) {
cahute_close_link(link);
return err;
}
}
*linkp = link;
return 0;
}
flags = 0;
if (args->no_init)
flags |= CAHUTE_USB_NOCHECK;
if (args->no_term)
flags |= CAHUTE_USB_NOTERM;
if ((err = find_usb_calculator(1, &bus, &address)))
return err;
if ((err = cahute_open_usb_link(&link, flags, bus, address)))
return err;
*linkp = link;
return 0;
}
/**
* Display device information.
*
* @param link Link to use to obtain the information.
* @return Cahute error.
*/
static int print_device_info(cahute_link *link) {
cahute_device_info *info;
int err;
if ((err = cahute_get_device_info(link, &info)))
return err;
/* Wiped out things */
if (~info->cahute_device_info_flags & CAHUTE_DEVICE_INFO_FLAG_PREPROG)
fprintf(
stderr,
"Warning: Preprogrammed ROM information looks wiped out!\n"
);
if (~info->cahute_device_info_flags & CAHUTE_DEVICE_INFO_FLAG_BOOTCODE)
fprintf(stderr, "Warning: Bootcode information looks wiped out!\n");
if (~info->cahute_device_info_flags & CAHUTE_DEVICE_INFO_FLAG_OS)
fprintf(stderr, "Warning: OS information looks wiped out!\n");
if (!info->cahute_device_info_username[0])
fprintf(stderr, "Warning: Username is not set.\n");
printf(
"CPU ID (probably out of date): %s\n",
info->cahute_device_info_cpuid
);
printf("Environnement ID: %s\n", info->cahute_device_info_hwid);
printf("Product ID: %s\n", info->cahute_device_info_product_id);
/* Preprogrammed ROM */
if (info->cahute_device_info_flags & CAHUTE_DEVICE_INFO_FLAG_PREPROG) {
printf(
"Preprogrammed ROM version: %02u.%02u",
info->cahute_device_info_rom_version.cahute_version_major,
info->cahute_device_info_rom_version.cahute_version_minor
);
printf(
"\nPreprogrammed ROM capacity: %luKiB\n",
info->cahute_device_info_rom_capacity / 1024
);
}
/* ROM and RAM */
printf(
"ROM capacity: %luKiB\n",
info->cahute_device_info_flash_rom_capacity / 1024
);
printf(
"RAM capacity: %luKiB\n",
info->cahute_device_info_ram_capacity / 1024
);
/* Bootcode */
if (info->cahute_device_info_flags & CAHUTE_DEVICE_INFO_FLAG_BOOTCODE) {
printf(
"Bootcode version: %02u.%02u",
info->cahute_device_info_bootcode_version.cahute_version_major,
info->cahute_device_info_bootcode_version.cahute_version_minor
);
printf(
"\nBootcode offset: 0x%08lX\n",
info->cahute_device_info_bootcode_offset
);
printf(
"Bootcode size: %luB\n",
info->cahute_device_info_bootcode_size
);
}
/* OS */
if (info->cahute_device_info_flags & CAHUTE_DEVICE_INFO_FLAG_OS) {
printf(
"OS version: %02d.%02d.%d%d%d%d",
info->cahute_device_info_os_version.cahute_version_major,
info->cahute_device_info_os_version.cahute_version_minor,
info->cahute_device_info_os_version.cahute_version_zone,
info->cahute_device_info_os_version.cahute_version_math,
info->cahute_device_info_os_version.cahute_version_status,
info->cahute_device_info_os_version.cahute_version_platform
);
printf("\nOS offset: 0x%08lX\n", info->cahute_device_info_os_offset);
printf("OS size: %luB\n", info->cahute_device_info_os_size);
}
/* Miscallenous information */
if (info->cahute_device_info_username[0])
printf("Username: %s\n", info->cahute_device_info_username);
if (info->cahute_device_info_organisation[0])
printf("Organisation: %s\n", info->cahute_device_info_organisation);
return 0;
}
/**
* Main function.
*
* @param ac Argument count.
* @param av Argument values.
*/
int main(int ac, char **av) {
cahute_link *link = NULL;
struct args args;
unsigned long flags;
int err, progress_displayed = 0;
if (!parse_args(ac, av, &args))
return 0;
if (args.command == COMMAND_LIST_SERIAL) {
/* The "list-devices" command does not require a link, and as such,
* is processed here independently from the others. */
int first = 0;
err = cahute_detect_serial(
(cahute_detect_serial_entry_func *)print_serial_device,
&first
);
if (err)
goto fail;
if (!first)
fprintf(stderr, "Could not find any devices.\n");
return 0;
}
/* Open the link using the provided args. */
err = open_link(&link, &args);
if (err)
goto fail;
switch (args.command) {
case COMMAND_INFO:
err = print_device_info(link);
break;
case COMMAND_IDLE:
/* Nothing to do! */
break;
case COMMAND_SEND:
flags = CAHUTE_SEND_FILE_FLAG_OPTIMIZE;
if (args.force)
flags |= CAHUTE_SEND_FILE_FLAG_FORCE;
err = cahute_send_file_to_storage(
link,
flags,
args.distant_target_directory_name,
args.distant_target_name,
args.storage_name,
args.local_source_fp,
&confirm_overwrite,
NULL,
args.nice_display ? (cahute_progress_func *)&display_progress : 0,
&progress_displayed
);
break;
case COMMAND_GET:
err = cahute_request_file_from_storage(
link,
args.distant_source_directory_name,
args.distant_source_name,
args.storage_name,
args.local_target_fp,
args.nice_display ? (cahute_progress_func *)&display_progress : 0,
&progress_displayed
);
break;
case COMMAND_COPY:
err = cahute_copy_file_on_storage(
link,
args.distant_source_directory_name,
args.distant_source_name,
args.distant_target_directory_name,
args.distant_target_name,
args.storage_name
);
break;
case COMMAND_DELETE:
err = cahute_delete_file_from_storage(
link,
args.distant_target_directory_name,
args.distant_target_name,
args.storage_name
);
break;
case COMMAND_LIST:
err = cahute_list_storage_entries(
link,
args.storage_name,
&print_storage_entry,
NULL
);
break;
case COMMAND_RESET:
err = cahute_reset_storage(link, args.storage_name);
break;
case COMMAND_OPTIMIZE:
err = cahute_optimize_storage(link, args.storage_name);
break;
default:
err = CAHUTE_ERROR_IMPL;
break;
}
if (err && err != CAHUTE_ERROR_NOOW)
goto fail;
if (progress_displayed)
puts("\b\b\b\b\b\bTransfer complete.");
cahute_close_link(link);
if (args.local_source_fp && args.local_source_path)
fclose(args.local_source_fp);
if (args.local_target_fp && args.local_target_path)
fclose(args.local_target_fp);
return 0;
fail:
if (progress_displayed)
puts("\b\b\b\b\b\bError !");
/* If a link has been initialized, we want to close it. */
if (link)
cahute_close_link(link);
/* If files have been opened when parsing args, we want to close them. */
if (args.local_source_fp && args.local_source_path)
fclose(args.local_source_fp);
if (args.local_target_fp && args.local_target_path)
fclose(args.local_target_fp);
/* If a local target path was defined, we want to remove it. */
if (args.local_target_path)
remove(args.local_target_path);
/* And now, to display an error corresponding to the obtained error. */
switch (err) {
case CAHUTE_ERROR_IMPL:
fprintf(stderr, error_notimplemented);
break;
case CAHUTE_ERROR_PRIV:
fprintf(stderr, error_noaccess);
break;
case CAHUTE_ERROR_NOT_FOUND:
fprintf(stderr, error_notfound);
break;
case CAHUTE_ERROR_INCOMPAT:
fprintf(stderr, error_unsupported);
break;
case CAHUTE_ERROR_GONE:
fprintf(stderr, error_disconnected);
break;
case CAHUTE_ERROR_TOO_MANY:
/* The message should have already been displayed in this case. */
break;
default:
fprintf(stderr, error_unplanned, av[0]);
}
return 1;
}

138
cli/p7.h Normal file
View File

@ -0,0 +1,138 @@
/* ****************************************************************************
* Copyright (C) 2016-2017, 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef P7_H
#define P7_H 1
#include <stdio.h>
#include "common.h"
#define DEFAULT_STORAGE "fls0"
#define COMMAND_LIST_SERIAL 1
#define COMMAND_SEND 2
#define COMMAND_GET 3
#define COMMAND_COPY 4
#define COMMAND_DELETE 5
#define COMMAND_LIST 6
#define COMMAND_RESET 7
#define COMMAND_OPTIMIZE 8
#define COMMAND_INFO 9
#define COMMAND_IDLE 10
/**
* Parsed argument structure.
*
* The combinations for the operations on the file system are the following:
*
* - SEND a {local_source_fp}'s content to a {distant_target_name}, in the
* {distant_target_directory_name} directory on {storage_name}.
* - GET {distant_source_name}, in the {distant_source_directory_name}
* directory on {storage_name}, to {local_target_fp}.
* - COPY {distant_source_name}, in the {distant_source_directory_name}
* directory, to {distant_target_name}, in the
* {distant_target_directory_name}, on {storage_name}.
* - DELETE {distant_target_name}, in the {distant_target_directory_name}
* directory, on {storage_name}.
* - LIST files in the {distant_target_directory_name} directory
* on {storage_name}.
* - RESET {storage_name}.
* - OPTIMIZE {storage_name}.
*
* General properties:
*
* @property command Selected subcommand.
* @property nice_display Whether nice display is enabled or not.
* @property force Whether to force overwrite or not.
*
* Connection properties:
*
* @property serial_flags Serial flags to define.
* @property serial_speed Speed to use.
* @property new_serial_flags Serial flags to update the connection to.
* @property new_serial_speed Serial speed to update the connection to.
* @property no_init If a connection is established, whether to initialize
* the connection (0) or not (1).
* @property no_term If a connection is established, whether to terminate
* the connection (0) or not (1).
* @property change_serial Whether to set new serial attributes or not.
* @property serial_name Serial device's name or path.
*
* Distant filesystem properties:
*
* @property storage_name Optional storage name for operations with the
* calculator.
* @property distant_source_directory_name Optional source directory name for
* copy, or directory name when getting a file.
* @property distant_source_name Source file name for copy, or file name
* when getting a file.
* @property distant_target_directory_name Optional target directory name for
* copy, or directory name when uploading a file.
* @property distant_target_name Target file name for copy, or file name
* when sending a file.
*
* Local filesystem properties:
*
* @property local_source_path Path to the local file when uploading a file.
* @property local_source_fp Local FILE object for uploading a file.
* @property local_target_path Path to the local file when downloading a file.
* @property local_target_fp Local FILE object for downloading a file.
*/
struct args {
int command;
int nice_display;
int force;
/* Connection-related parameters. */
unsigned long serial_flags;
unsigned long serial_speed;
unsigned long new_serial_flags;
unsigned long new_serial_speed;
int no_init;
int no_term;
int change_serial;
char const *serial_name;
/* Calculator storage related parameters. */
char const *storage_name;
char const *distant_source_directory_name;
char const *distant_source_name;
char const *distant_target_directory_name;
char const *distant_target_name;
/* Local data.
* The local source fp may be defined without the local source path being
* defined, which means we are actually targetting a special stream,
* such as stdin. Same for the target with stdout or stderr. */
char const *local_source_path;
char const *local_target_path;
FILE *local_source_fp;
FILE *local_target_fp;
};
extern int parse_args(int ac, char **av, struct args *args);
#endif /* P7_H */

745
cli/p7_args.c Normal file
View File

@ -0,0 +1,745 @@
/* ****************************************************************************
* Copyright (C) 2016-2017, 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "p7.h"
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
static char const version_message[] =
"p7 - from Cahute v" CAHUTE_VERSION
" (licensed under CeCILL 2.1)\n"
"\n"
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or\n"
"FITNESS FOR A PARTICULAR PURPOSE.";
static char const help_main[] =
"Usage: %s\n"
" [--version|-v] [--help|-h] [-l|--log <level>]\n"
" [--com <device>] [--use <params>] [--set <params>] [--reset]\n"
" [--no-init] [--no-exit]\n"
" <subcommand> [options...]\n"
"\n"
"Subcommands you can use are:\n"
" help Display the help page of the command.\n"
" version Display the version message.\n"
" list-devices List available serial devices.\n"
" info Get information about the calculator.\n"
" idle Do nothing once the link is established.\n"
" send Send a file to the calculator.\n"
" get Get a file from the calculator.\n"
" copy Copy a file into another on the calculator.\n"
" delete Delete a file on the calculator.\n"
" list List files on the distant filesystem.\n"
" reset Reset the flash memory.\n"
" optimize Optimize the distant filesystem.\n"
"\n"
"General options:\n"
" -h, --help Display the help page of the (sub)command and quit.\n"
" -v, --version Display the version message and quit.\n"
" -l, --log <level> Logging level to set (default: %s).\n"
" One of: info, warning, error, fatal, none.\n"
"\n"
"Link-related options:\n"
" --com <device> Path or name of the serial device with which to\n"
" communicate. If this option isn't used, the\n"
" program will use USB to find the calculator.\n "
" --use <settings> Serial settings to use, when the link is established\n"
" over a serial link (i.e. when used with `--com`).\n"
" For example, \"9600N2\" represents 9600 bauds, no\n"
" parity, and two stop bits.\n"
" --set <settings> Serial settings to negotiate with the calculator\n"
" (when used with `--com`).\n"
" The string has the same format than for `--use`.\n"
" --reset Shorthand option for `--set 9600N2`.\n"
" --no-init Disable the initiation handshake when the link is\n"
" established, for chaining multiple p7 subcommands.\n"
" --no-exit Disable the termination handshake when the link is\n"
" closed, for chaining multiple p7 subcommands.\n"
"\n"
"Type \"%s <subcommand> --help\" for some help "
"about the subcommand.\n"
"\n"
"For guides, topics and reference, consult the documentation:\n"
" " CAHUTE_URL
"\n"
"\n"
"For reporting issues and vulnerabilities, consult the following guide:\n"
" " CAHUTE_ISSUES_URL "\n";
#define SUBCOMMAND_FOOTER \
"\nType \"%s --help\" for other subcommands and general options.\n"
static char const help_list_devices[] =
"Usage: %s list-devices\n"
"List serial devices.\n" SUBCOMMAND_FOOTER;
static char const help_info[] =
"Usage: %s info\n"
"Get information about the calculator.\n" SUBCOMMAND_FOOTER;
static char const help_idle[] =
"Usage: %s idle\n"
"Do nothing while the link is active.\n"
"\n"
"This subcommand is useful when chaining p7 subcommands, to dedicate a\n"
"p7 call to only initiate or terminate the link, or negotiate new\n"
"serial settings.\n" SUBCOMMAND_FOOTER;
static char const help_send[] =
"Usage: %s send [options...] <local file>\n"
"Send a file to the calculator.\n"
"\n"
"Available options are:\n"
" -# Display a nice progress bar.\n"
" -f, --force Force overwriting if relevant.\n"
" -o, --output <name>\n"
" Output filename on the calculator.\n"
" By default, the output file name is the base name\n"
" of the provided local file path.\n"
" -d, --directory <dir>\n"
" On-calc directory name in which the file will be\n"
" stored. By default, the file is stored at root.\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
static char const help_get[] =
"Usage: %s get [options...] <on-calc filename>\n"
"Request a file from the calculator.\n"
"\n"
"Available options are:\n"
" -# Display a nice progress bar.\n"
" -o, --output <name>\n"
" Output local file path, absolute or relative to\n"
" the working directory. By default, the file is\n"
" stored in the working directory with the name\n"
" it had on the calculator.\n"
" -d, --directory <dir>\n"
" On-calc directory name from which to get the file.\n"
" By default, the file is retrieved from root.\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
static char const help_copy[] =
"Usage: %s copy [options...] <source file> <dest file>\n"
"Copy a file into the other on the calculator.\n"
"\n"
"Available options are:\n"
" -# Display a nice progress bar.\n"
" -d, --directory <srcdir>\n"
" On-calc directory name in which the source file is\n"
" located. By default, root is used.\n"
" -t, --to <dstdir> On-calc directory name in which the file should be\n"
" copied to. By default, root is used.\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
static char const help_delete[] =
"Usage: %s delete [options...] <on-calc filename>\n"
"Delete a file on the calculator.\n"
"\n"
"Available options are:\n"
" -d, --directory <dir>\n"
" On-calc directory name from which to delete the\n"
" file. By default, the file is deleted from root.\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
static char const help_list[] =
"Usage: %s list [options...]\n"
"List files on the distant filesystem.\n"
"\n"
"Available options are:\n"
" -d, --directory <dir>\n"
" On-calc directory name from which to list\n"
" files. By default, files are listed from every\n"
" directory, including root.\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
static char const help_reset[] =
"Usage: %s reset\n"
"Reset the distant filesystem.\n"
"\n"
"Available options are:\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
static char const help_optimize[] =
"Usage: %s optimize\n"
"Optimize the distant filesystem.\n"
"\n"
"Available options are:\n"
" --storage <abc0> Storage device with which to interact (fls0,\n"
" crd0). By default, this option is set to "
"'" DEFAULT_STORAGE "'.\n" SUBCOMMAND_FOOTER;
/**
* Short options for getopt_long().
*/
static char const *short_options = "hvfo:d:t:l:#";
/**
* Long options for getopt_long().
*/
static struct option const long_options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"com", required_argument, NULL, 'c'},
{"storage", required_argument, NULL, 's'},
{"force", no_argument, NULL, 'f'},
{"output", required_argument, NULL, 'o'},
{"directory", required_argument, NULL, 'd'},
{"to", required_argument, NULL, 't'},
{"no-init", no_argument, NULL, 'i'},
{"no-start", no_argument, NULL, 'i'},
{"no-exit", no_argument, NULL, 'e'},
{"no-term", no_argument, NULL, 'e'},
{"set", required_argument, NULL, 'S'},
{"reset", no_argument, NULL, 'R'},
{"use", required_argument, NULL, 'U'},
{"log", required_argument, NULL, 'l'},
/* sentinel */
{NULL, 0, NULL, 0}
};
/**
* Parse serial attributes.
*
* The raw string format is "{speed}{parity}{stop bits}", where:
*
* - Speed is expressed in bauds, e.g. "9600".
* - Parity is either "E" for even, "O" for odd and "N" for disabled.
* - Stop bits is either "1" or "2".
*
* An example string is "9600N2" for 9600 bauds, no parity and 2 stop bits.
*
* @param raw Raw serial attributes.
* @param flagsp Pointer to the flags to define.
* @param speedp Pointer to the speed to define.
* @return Whether an error has occurred (1) or not (0).
*/
static int parse_serial_attributes(
char const *raw,
unsigned long *flagsp,
unsigned long *speedp
) {
char const *s;
unsigned long speed = 0;
unsigned long flags = 0;
for (s = raw; *s >= '0' && *s <= '9'; s++)
speed = speed * 10 + *s - '0';
if (s == raw)
return 1;
switch (speed) {
case 300:
case 600:
case 1200:
case 2400:
case 4800:
case 9600:
case 19200:
case 38400:
case 57600:
case 115200:
case 230400:
case 460800:
break;
default:
return 1;
}
if (*s == 'N')
flags |= CAHUTE_SERIAL_PARITY_OFF;
else if (*s == 'E')
flags |= CAHUTE_SERIAL_PARITY_EVEN;
else if (*s == 'O')
flags |= CAHUTE_SERIAL_PARITY_ODD;
else
return 1;
s++;
if (*s == '1')
flags |= CAHUTE_SERIAL_STOP_ONE;
else if (*s == '2')
flags |= CAHUTE_SERIAL_STOP_TWO;
else
return 1;
s++;
if (*s) /* String should be terminated. */
return 1;
*speedp = speed;
*flagsp = flags;
return 0;
}
/**
* Check an on-calc directory name.
*
* @param name Directory name to check.
* @return 1 if all checks pass, 0 otherwise.
*/
static inline int check_directory_name(char const *name) {
size_t n;
if (!name)
return 1;
n = strnlen(name, 9);
if (n > 9)
return 0;
for (; n--; name++)
if (!isascii(*name) || *name == '/' || *name == '\\'
|| (!isgraph(*name) && !isblank(*name)))
return 0;
return 1;
}
/**
* Check an on-calc file name.
*
* @param name Directory name to check.
* @return 1 if all checks pass, 0 otherwise.
*/
static inline int check_file_name(char const *name) {
size_t n;
if (!name)
return 1;
n = strnlen(name, 13);
if (n > 12)
return 0;
for (; n--; name++)
if (!isascii(*name) || *name == '/' || *name == '\\'
|| (!isgraph(*name) && !isblank(*name)))
return 0;
return 1;
}
/**
* Parse command-line parameters, and handle help and version messages.
*
* Note that since we use getopt_long(), the argv array is actually reorganized
* to move positional parameters at the end of the array, hence why argv
* is of "char **" type, and not "char const * const *".
*
* @param argc Argument count, as provided to main().
* @param argv Argument values, as provided to main().
* @param args Parsed argument structure to feed for use by caller.
* @return Whether parameters were successfully parsed (1), or not (0).
*/
int parse_args(int argc, char **argv, struct args *args) {
char const *command = argv[0], *subcommand;
char **params;
char const *o_directory = NULL;
char const *o_target_directory = NULL;
char const *o_output = NULL;
char const *o_storage = DEFAULT_STORAGE;
int option, help = 0, param_count;
/* Default parsed arguments.
* By default, the serial speed is defined as 9600N2. */
args->command = COMMAND_IDLE;
args->nice_display = 1;
args->serial_flags = CAHUTE_SERIAL_PARITY_OFF | CAHUTE_SERIAL_STOP_TWO;
args->serial_speed = 9600;
args->new_serial_flags = CAHUTE_SERIAL_PARITY_OFF | CAHUTE_SERIAL_STOP_TWO;
args->new_serial_speed = 9600;
args->no_init = 0;
args->no_term = 0;
args->change_serial = 0;
args->serial_name = NULL;
args->storage_name = NULL;
args->distant_source_directory_name = NULL;
args->distant_source_name = NULL;
args->distant_target_directory_name = NULL;
args->distant_target_name = NULL;
args->force = 0;
args->local_source_path = NULL;
args->local_target_path = NULL;
args->local_source_fp = NULL;
args->local_target_fp = NULL;
opterr = 0;
while (1) {
option = getopt_long(argc, argv, short_options, long_options, NULL);
if (option < 0)
break;
switch (option) {
case 'h':
/* -h, --help: display the help message and quit.
* This is not done immediately because the displayed help
* message depends on the parameters. */
help = 1;
break;
case 'v':
/* -v, --version: display the version message and quit. */
puts(version_message);
return 0;
case 'f':
/* -f, --force: overwrite without asking. */
args->force = 1;
break;
case '#':
/* -#: enable the loading bar. */
args->nice_display = 1;
break;
case 'l':
/* -l, --log: set the logging level. */
set_log_level(optarg);
break;
case 'o':
/* -o, --output: set the output filename.
*
* This can have multiple meanings depending on the subcommand:
*
* - With 'send' as 'distant_target_name'.
* - With 'get', as 'distant_source_name. */
o_output = optarg;
break;
case 'd':
/* -d, --directory: set the output directory on the calculator.
*
* This can have multiple meanings depending on the subcommand:
*
* - With 'send', as 'distant_target_directory_name'.
* - With 'get' and 'copy', as 'distant_source_directory_name'. */
o_directory = optarg;
break;
case 't':
/* -t, --to: set the destination directory for copy.
*
* This is only the 'distant_target_directory_name' for 'copy'. */
o_target_directory = optarg;
break;
case 'c':
/* --com: set the serial port. */
args->serial_name = optarg;
break;
case 's':
/* --storage: set the storage. */
o_storage = optarg;
break;
case 'i':
/* --no-init: disable link initialization. */
args->no_init = 1;
break;
case 'e':
/* --no-exit: disable link termination. */
args->no_term = 1;
break;
case 'U':
/* --use: use initial serial settings. */
if (parse_serial_attributes(
optarg,
&args->serial_flags,
&args->serial_speed
)) {
fprintf(stderr, "-u, --use: invalid format!\n");
return 0;
}
break;
case 'S':
/* --set: set serial settings to negotiate with the calculator. */
if (parse_serial_attributes(
optarg,
&args->new_serial_flags,
&args->new_serial_speed
)) {
fprintf(stderr, "-s, --set: invalid format!\n");
return 0;
}
args->change_serial = 1;
break;
case 'R':
/* --reset: reset serial settings. */
args->new_serial_flags =
CAHUTE_SERIAL_PARITY_OFF | CAHUTE_SERIAL_STOP_TWO;
args->new_serial_speed = 9600;
args->change_serial = 1;
break;
case '?':
/* Erroneous option usage. */
if (optopt == 'o')
fprintf(stderr, "-o, --output: expected an argument\n");
else if (optopt == 'd')
fprintf(stderr, "-d, --directory: expected an argument\n");
else if (optopt == 't')
fprintf(stderr, "-t, --to: expected an argument\n");
else if (optopt == 'c')
fprintf(stderr, "--com: expected an argument\n");
else if (optopt == 's')
fprintf(stderr, "--storage: expected an argument\n");
else
/* We ignore unknown options. */
break;
return 0;
}
}
param_count = argc - optind;
params = &argv[optind];
if (!param_count || !strcmp(params[0], "help")) {
printf(help_main, command, get_current_log_level(), command);
return 0;
}
subcommand = params[0];
params++;
param_count--;
if (!strcmp(subcommand, "version")) {
puts(version_message);
return 0;
}
if (!strcmp(subcommand, "list-devices")) {
if (help || param_count != 0) {
printf(help_list_devices, command, command);
return 0;
}
args->command = COMMAND_LIST_SERIAL;
} else if (!strcmp(subcommand, "send")) {
if (help || param_count != 1) {
printf(help_send, command, command);
return 0;
}
if (!o_output) {
/* By default, we want to determine the output name from the
* local path. */
o_output = strrchr(params[0], '/');
if (!o_output)
o_output = params[0];
}
args->command = COMMAND_SEND;
args->storage_name = o_storage;
args->local_source_path = params[0];
args->distant_target_directory_name = o_directory;
args->distant_target_name = o_output;
} else if (!strcmp(subcommand, "get")) {
if (help || param_count != 1) {
printf(help_get, command, command);
return 0;
}
if (o_output == NULL)
/* By default, we want the local name to be the same as the
* distant file name. */
o_output = params[0];
args->command = COMMAND_GET;
args->storage_name = o_storage;
args->distant_source_directory_name = o_directory;
args->distant_source_name = params[0];
if (!strcmp(o_output, "-")) /* Standard output. */
args->local_target_fp = stdout;
else
args->local_target_path = o_output;
} else if (!strcmp(subcommand, "copy") || !strcmp(subcommand, "cp")) {
if (help || param_count != 2) {
printf(help_copy, command, command);
return 0;
}
args->command = COMMAND_COPY;
args->storage_name = o_storage;
args->distant_source_directory_name = o_directory;
args->distant_source_name = params[0];
args->distant_target_directory_name = o_target_directory;
args->distant_target_name = params[1];
} else if (!strcmp(subcommand, "delete") || !strcmp(subcommand, "del")) {
if (help || param_count != 1) {
printf(help_delete, command, command);
return 0;
}
args->command = COMMAND_DELETE;
args->storage_name = o_storage;
args->distant_target_directory_name = o_directory;
args->distant_target_name = params[0];
} else if (!strcmp(subcommand, "list") || !strcmp(subcommand, "ls")) {
if (help || param_count != 0) {
printf(help_list, command, command);
return 0;
}
args->command = COMMAND_LIST;
args->storage_name = o_storage;
args->distant_target_directory_name = o_directory;
} else if (!strcmp(subcommand, "reset")) {
if (help || param_count != 0) {
printf(help_reset, command, command);
return 0;
}
args->command = COMMAND_RESET;
args->storage_name = o_storage;
} else if (!strcmp(subcommand, "optimize")) {
if (help || param_count != 0) {
printf(help_optimize, command, command);
return 0;
}
args->command = COMMAND_OPTIMIZE;
args->storage_name = o_storage;
} else if (!strcmp(subcommand, "info")) {
if (help || param_count != 0) {
printf(help_info, command, command);
return 0;
}
args->command = COMMAND_INFO;
} else if (!strcmp(subcommand, "idle") || !strcmp(subcommand, "laze")) {
if (help || param_count != 0) {
printf(help_idle, command, command);
return 0;
}
args->command = COMMAND_IDLE;
} else {
/* The subcommand is unknown. */
printf(help_main, command, get_current_log_level(), command);
return 0;
}
if (args->storage_name
&& (strlen(args->storage_name) != 4 || !isascii(args->storage_name[0])
|| !islower(args->storage_name[0])
|| !isascii(args->storage_name[1])
|| !islower(args->storage_name[1])
|| !isascii(args->storage_name[2])
|| !isascii(args->storage_name[2])
|| !isascii(args->storage_name[3])
|| !isdigit(args->storage_name[3]))) {
fprintf(stderr, "Invalid storage name format.\n");
return 0;
}
if (!check_directory_name(args->distant_source_directory_name)) {
fprintf(stderr, "Invalid source directory name format.\n");
return 0;
}
if (!check_file_name(args->distant_source_name)) {
fprintf(stderr, "Invalid source file name format.\n");
return 0;
}
if (!check_directory_name(args->distant_target_directory_name)) {
fprintf(stderr, "Invalid target directory name format.\n");
return 0;
}
if (!check_file_name(args->distant_target_name)) {
fprintf(stderr, "Invalid target file name format.\n");
return 0;
}
/* Open the local source path if a path is given. */
if (args->local_source_path && !args->local_source_fp) {
args->local_source_fp = fopen(args->local_source_path, "r");
if (!args->local_source_fp) {
fprintf(
stderr,
"Can't open '%s': %s\n",
args->local_source_path,
strerror(errno)
);
return 0;
}
}
/* Open the local target path if a path is given. */
if (args->local_target_path && !args->local_target_fp) {
args->local_target_fp = fopen(args->local_target_path, "w+");
if (!args->local_target_fp) {
fprintf(
stderr,
"Can't open '%s': %s\n",
args->local_target_path,
strerror(errno)
);
if (args->local_source_path && args->local_source_fp)
fclose(args->local_source_fp);
return 0;
}
}
return 1;
}

494
cli/p7screen.c Normal file
View File

@ -0,0 +1,494 @@
/* ****************************************************************************
* Copyright (C) 2016-2017, 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "p7screen.h"
#include <string.h>
#include <SDL.h>
static Uint32 const dual_pixels[] = {0xFFFFFF, 0xAAAAAA, 0x777777, 0x000000};
/**
* Display cookie.
*
* @property window Window that contains the surface.
* @property renderer Renderer for the window.
* @property texture Texture that covers the window.
* @property saved_width Saved width from when the window was first opened.
* @property saved_height Saved height from when the window was first opened.
* @property zoom Zoom with which to draw the window.
*/
struct display_cookie {
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
int saved_width;
int saved_height;
int zoom;
};
static char const error_notfound[] =
"Could not connect to the calculator.\n"
"- Is it plugged in and in PROJ mode?\n"
"- Have you tried unplugging, plugging and selecting Projector on "
"pop-up?\n"
"- Have you tried changing the cable?\n";
static char const error_noaccess[] =
"Could not get access to the calculator.\n"
"Install the appropriate udev rule, or run as root.\n";
static char const error_unplanned[] =
"The calculator didn't act as planned.\n"
"Stop receive mode on calculator and start it again before re-running "
"p7screen.\n";
/**
* Update the texture pixels with the frame contents.
*
* Both the destination format and all source formats are organized into
* lines first, columns seconds, which means a picture with pixels ABCDEF
* represent the following:
*
* A B C
* D E F
*
* However, the destination format also includes a zoom, which means that
* the above picture will need to be represented the following for a zoom
* of 3:
*
* * [A] A A B B B C C C
* / A A A B B B C C C
* / A A A B B B C C C
* * [D] D D E E E F F F
* / D D D E E E F F F
* / D D D E E E F F F
*
* For every line we read from the source format, we compute the origin
* of the destination line into ``oy``, which will be:
*
* - The index of the line, starting at 0...
* - ... multiplied by the zoom (number of destination lines per source
* line)...
* - ... multiplied by the width (number of pixels in one line)...
* - ... multiplied by the zoom again (number of destination pixels per
* source pixel).
*
* Once we have this, the idea is that we only compute the first destination
* line per source line, and copy it for the rest of the destination lines
* corresponding to the same source line. In the example, lines prefixed
* with "*" are computed directly, and lines prefixed with "/" are copied
* from lines prefixed with "*".
*
* Then, for every pixel in the source frame, we copy it zoom times into
* the destination frame.
*
* The basic template for every case is the following:
*
* for (int y = 0, oy = 0; y < height; y++, oy += zoom_line_size) {
* for (int x = 0, ox = 0; x < width; x++, ox += zoom) {
* Uint32 pixel = ...; // format-specific stuff
*
* for (int dx = zoom - 1; dx >= 0; dx--)
* pixels[oy + ox + dx] = pixel;
* }
*
* for (int py = oy + zoom_line_size - line_size; py > oy;
* py -= line_size)
* memcpy(&pixels[py], &pixels[oy], line_size << 2);
* }
*
* Note that we use 'line_size << 2' at the end, and not 'line_size',
* because we are dealing with 32-bit integers and not 8-bit integers here,
* so we need to multiply by 4 the memory size to copy.
*
* Also note that we prefer the "one loop by format" instead of "one loop
* for every format", because not having additional conditions in the main
* body of the loop makes it faster.
*
* Also note that since we're on picture format conversions, with potentially
* a lot of pixels, we want to use arithmetic or bitwise operations as much
* as possible, since conditional operations disrupt the pipeline.
*
* @param pixels Pixels array to update, using sRGB colors on 32-bits.
* @param frame Frame to update the pixels with.
* @param zoom Zoom to draw the frame with.
*/
static void
update_texture_pixels(Uint32 *pixels, cahute_frame const *frame, int zoom) {
int width = frame->cahute_frame_width;
int height = frame->cahute_frame_height;
int line_size = zoom * width;
int zoom_line_size = line_size * zoom;
int y, oy, x, ox, dx, py, mask;
cahute_u8 const *data = frame->cahute_frame_data;
cahute_u8 const *data2;
switch (frame->cahute_frame_format) {
case CAHUTE_PICTURE_FORMAT_1BIT_MONO:
for (y = 0, oy = 0; y < height; y++, oy += zoom_line_size) {
/* The mask will be right-shifted every pixel, unless it's 1,
* which leads to the mask being reset to 128 using
* '(mask & 1) << 7'. */
mask = 128;
for (x = 0, ox = 0; x < width; x++, ox += zoom) {
Uint32 pixel = *data & mask ? 0x000000 : 0xFFFFFF;
for (dx = zoom - 1; dx >= 0; dx--)
pixels[oy + ox + dx] = pixel;
/* Go to the next byte if we're resetting the mask from
* 1 back to 128. */
data += mask & 1;
mask = (mask >> 1) | ((mask & 1) << 7);
}
for (py = oy + zoom_line_size - line_size; py > oy;
py -= line_size)
memcpy(&pixels[py], &pixels[oy], line_size << 2);
/* The start of the next line is aligned, if we are not aligned
* already, we need to align ourselves. */
data += (~mask & 128) >> 7;
}
break;
case CAHUTE_PICTURE_FORMAT_1BIT_DUAL:
data2 = data + height * ((width >> 8) + !!(width & 7));
for (y = 0, oy = 0; y < height; y++, oy += zoom_line_size) {
/* Same logic as for 1-bit monochrome encoding. */
mask = 128;
for (x = 0, ox = 0; x < width; x++, ox += zoom) {
/* We obtain the first bit and the second bit, then we need
* to place the first bit in the before-last rank (i.e. 0bX0)
* and the second bit to the last rank (i.e. 0bX).
*
* In order to do this, we determine the position of the
* bit in the original byte using '7 - (x & 7)'. Here's
* the table showing that it works:
*
* +-------+-------------+------+
* | x & 7 | 7 - (x & 7) | Rank |
* +-------+-------------+------+
* | 0 | 7 | 7 |
* | 1 | 6 | 6 |
* | 2 | 5 | 5 |
* | 3 | 4 | 4 |
* | 4 | 3 | 3 |
* | 5 | 2 | 2 |
* | 6 | 1 | 1 |
* | 7 | 0 | 0 |
* +-------+-------------+-----+
*
* Since both the first and second bit have the same rank,
* we can just shift the first bit a rank to the left, then
* shift the whole number down to rank 0 to obtain the 2-bit
* result.
*
* NOTE: Before shifting the first bit left, we need to
* ensure that the operation is run on an integer more
* than 8-bit long, otherwise the bit will be lost. */
int index = *data & mask;
index = (index << 1) | (*data2 & mask);
index >>= 7 - (x & 7);
Uint32 pixel = dual_pixels[index & 3];
for (dx = zoom - 1; dx >= 0; dx--)
pixels[oy + ox + dx] = pixel;
data += mask & 1;
data2 += mask & 1;
mask = (mask >> 1) | ((mask & 1) << 7);
}
for (py = oy + zoom_line_size - line_size; py > oy;
py -= line_size)
memcpy(&pixels[py], &pixels[oy], line_size << 2);
data += (~mask & 128) >> 7;
}
break;
case CAHUTE_PICTURE_FORMAT_4BIT_RGB_PACKED:
mask = 240; /* 0b11110000 */
for (y = 0, oy = 0; y < height; y++, oy += zoom_line_size) {
for (x = 0, ox = 0; x < width; x++, ox += zoom) {
int raw_value = *data & mask;
Uint32 pixel = 0x000000;
/* R bit in high and low nibble is 136 = 0b1000 1000.
* G bit in high and low nibble is 68 = 0b0100 0100.
* B bit in high and low nibble is 34 = 0b0010 0010. */
if (raw_value & 136)
pixel |= 0xFF0000;
if (raw_value & 68)
pixel |= 0x00FF00;
if (raw_value & 34)
pixel |= 0x0000FF;
for (dx = zoom - 1; dx >= 0; dx--)
pixels[oy + ox + dx] = pixel;
data += mask & 1;
mask = ~mask & 255;
}
for (py = oy + zoom_line_size - line_size; py > oy;
py -= line_size)
memcpy(&pixels[py], &pixels[oy], line_size << 2);
/* No end-of-line conditional re-alignment here, we are on a
* packed format. */
}
break;
case CAHUTE_PICTURE_FORMAT_16BIT_R5G6B5:
for (y = 0, oy = 0; y < height; y++, oy += zoom_line_size) {
for (x = 0, ox = 0; x < width; x++, ox += zoom) {
/* We have a 16-bit integer being 0bRRRRRGGGGGGBBBBB.
* We need to extract these using masks, and place it
* at the right ranks in the resulting 24-bit RGB pixel. */
unsigned long raw = (data[0] << 8) | data[1];
Uint32 pixel = ((raw >> 11) & 31) << 19
| ((raw >> 5) & 63) << 10 | (raw & 31) << 3;
for (dx = zoom - 1; dx >= 0; dx--)
pixels[oy + ox + dx] = pixel;
data += 2;
}
for (py = oy + zoom_line_size - line_size; py > oy;
py -= line_size)
memcpy(&pixels[py], &pixels[oy], line_size << 2);
}
break;
default:
fprintf(
stderr,
"!! Unhandled picture format %d\n",
frame->cahute_frame_format
);
}
}
/**
* Callback to display the screen frame.
*
* @param cookie Display cookie.
* @param frame Frame to display.
* @return Whether we want to interrupt the flow.
*/
static int
display_frame(struct display_cookie *cookie, cahute_frame const *frame) {
int width, height, format, zoom;
width = frame->cahute_frame_width;
height = frame->cahute_frame_height;
format = frame->cahute_frame_format;
zoom = cookie->zoom;
if (format != CAHUTE_PICTURE_FORMAT_1BIT_MONO
&& format != CAHUTE_PICTURE_FORMAT_1BIT_DUAL
&& format != CAHUTE_PICTURE_FORMAT_4BIT_RGB_PACKED
&& format != CAHUTE_PICTURE_FORMAT_16BIT_R5G6B5) {
fprintf(stderr, "Unsupported format %d.\n", format);
return 1;
}
if (!cookie->window) {
/* We haven't got a window, our objective is to create one,
* with a renderer and a texture. First, let's create the
* window. */
cookie->window = SDL_CreateWindow(
"p7screen",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width * zoom,
height * zoom,
0
);
if (!cookie->window) {
fprintf(
stderr,
"Couldn't create the window: %s\n",
SDL_GetError()
);
return 1;
}
/* Then let's create the renderer. */
cookie->renderer =
SDL_CreateRenderer(cookie->window, -1, SDL_RENDERER_SOFTWARE);
if (!cookie->renderer) {
fprintf(
stderr,
"Couldn't create the renderer: %s\n",
SDL_GetError()
);
return 1;
}
/* Finally, create the texture we're gonna use for drawing
* the picture as a classic ARGB pixel matric (8 bits per
* component). */
cookie->texture = SDL_CreateTexture(
cookie->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
width * zoom,
height * zoom
);
if (!cookie->texture) {
fprintf(
stderr,
"Couldn't create the texture: %s\n",
SDL_GetError()
);
return 1;
}
cookie->saved_width = width;
cookie->saved_height = height;
puts("Turn off your calculator (SHIFT+AC) when you have finished.\n");
} else if (cookie->saved_width != width || cookie->saved_height != height) {
/* The dimensions have changed somehow, we don't support this. */
fprintf(stderr, "Unmanaged dimensions changed.\n");
return 1;
}
/* Copy the data. */
{
Uint32 *texture_pixels;
int pitch;
SDL_LockTexture(
cookie->texture,
NULL,
(void **)&texture_pixels,
&pitch
);
update_texture_pixels(texture_pixels, frame, cookie->zoom);
SDL_UnlockTexture(cookie->texture);
}
SDL_RenderCopy(cookie->renderer, cookie->texture, NULL, NULL);
SDL_RenderPresent(cookie->renderer);
return 0;
}
/**
* Main entry point of the program.
*
* @param argc Argument count.
* @param argv Argument values.
* @return Exit status code.
*/
int main(int ac, char **av) {
cahute_link *link = NULL;
struct args args;
struct display_cookie cookie;
int bus, address, err, ret = 0;
if (!parse_args(ac, av, &args))
return 0;
if ((err = find_usb_calculator(1, &bus, &address))
|| (err = cahute_open_usb_link(&link, CAHUTE_USB_OHP, bus, address))) {
switch (err) {
case CAHUTE_ERROR_NOT_FOUND:
fprintf(stderr, error_notfound);
break;
case CAHUTE_ERROR_PRIV:
fprintf(stderr, error_noaccess);
break;
default:
fprintf(stderr, error_unplanned);
break;
}
return 1;
}
/* Initialize the SDL. */
if (SDL_Init(SDL_INIT_VIDEO)) {
fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
cahute_close_link(link);
return 3;
}
atexit(SDL_Quit);
cookie.window = NULL;
cookie.renderer = NULL;
cookie.texture = NULL;
cookie.saved_width = -1;
cookie.saved_height = -1;
cookie.zoom = args.zoom;
err = cahute_receive_screen(
link,
(cahute_process_frame_func *)display_frame,
&cookie
);
if (err) {
ret = 1;
switch (err) {
case CAHUTE_ERROR_INT:
/* Interrupted; the error message was already displayed. */
break;
case CAHUTE_ERROR_GONE:
ret = 0;
break;
default:
fprintf(stderr, error_unplanned);
break;
}
}
if (cookie.texture)
SDL_DestroyTexture(cookie.texture);
if (cookie.renderer)
SDL_DestroyRenderer(cookie.renderer);
if (cookie.window)
SDL_DestroyWindow(cookie.window);
cahute_close_link(link);
return ret;
}

48
cli/p7screen.h Normal file
View File

@ -0,0 +1,48 @@
/* ****************************************************************************
* Copyright (C) 2016-2017, 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef P7SCREEN_H
#define P7SCREEN_H 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#define DEFAULT_ZOOM 2
/**
* Parsed argument structure.
*
* @property zoom Zoom from 1 to 16 (i.e. a distant pixel is 1x1 to 16x16).
*/
struct args {
int zoom;
};
extern int parse_args(int ac, char **av, struct args *args);
#endif /* P7SCREEN_H */

155
cli/p7screen_args.c Normal file
View File

@ -0,0 +1,155 @@
/* ****************************************************************************
* Copyright (C) 2016-2017, 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "p7screen.h"
#include <stdlib.h>
#include <ctype.h>
#include <getopt.h>
static char const version_message[] =
"p7screen - from Cahute v" CAHUTE_VERSION
" (licensed under CeCILL 2.1)\n"
"\n"
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or\n"
"FITNESS FOR A PARTICULAR PURPOSE.";
static char const help_message[] =
"Usage: %s\n"
" [--help|-h] [--version|-v]\n"
"\n"
"Displays the streamed screen from a CASIO calculator connected by USB.\n"
"\n"
"Options are:\n"
" -h, --help Display this help page\n"
" -v, --version Displays the version\n"
" -l, --log <level> Logging level to set (default: %s).\n"
" One of: info, warning, error, fatal, none.\n"
" -z, --zoom <zoom> Change the zoom (1 to 16)\n"
" By default, the zoom will be %d.\n"
"\n"
"For guides, topics and reference, consult the documentation:\n"
" " CAHUTE_URL
"\n"
"\n"
"For reporting issues and vulnerabilities, consult the following guide:\n"
" " CAHUTE_ISSUES_URL "\n";
/**
* Short options for getopt_long().
*/
char const *short_options = "hvz:l:";
/**
* Long options for getopt_long().
*/
struct option const long_options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"zoom", required_argument, NULL, 'z'},
{"log", required_argument, NULL, 'l'},
{NULL, 0, NULL, 0}
};
/**
* Parse command-line parameters, and handle help and version messages.
*
* Note that since we use getopt_long(), the argv array is actually
* reorganized to move positional parameters at the end of the array, hence
* why argv is of "char **" type, and not "char const * const *".
*
* @param argc Argument count, as provided to main().
* @param argv Argument values, as provided to main().
* @param args Parsed argument structure to feed for use by the caller.
* @return Whether parameters were successfully parsed (1), or not (0).
*/
int parse_args(int argc, char **argv, struct args *args) {
int help = 0, zoom, option;
/* Default parsed arguments. */
args->zoom = DEFAULT_ZOOM;
opterr = 0;
while (1) {
option = getopt_long(argc, argv, short_options, long_options, NULL);
if (option < 0)
break;
switch (option) {
case 'h':
/* -h, --help: display the help message and quit. */
help = 1;
break;
case 'v':
/* -v, --version: display the version message and quit. */
puts(version_message);
return 0;
case 'z':
/* --zoom: set the zoom as an integer between 1 and 16. */
zoom = atoi(optarg);
if (zoom < 1 || zoom > 16) {
fprintf(stderr, "-z, --zoom: should be between 1 and 16\n");
return 0;
}
args->zoom = zoom;
break;
case 'l':
/* -l, --log: set the logging level. */
set_log_level(optarg);
break;
case '?':
/* Erroneous option usage. */
if (optopt == 'z')
fprintf(stderr, "-z, --zoom: expected an argument\n");
else
/* We ignore unknown options. */
break;
return 0;
}
}
/* p7screen is used without parameters.
* If there is any, we want to print the help and quit. */
if (argc - optind)
help = 1;
/* If we want to display the help message, do it here! */
if (help) {
printf(help_message, argv[0], get_current_log_level(), DEFAULT_ZOOM);
return 0;
}
return 1;
}

154
cli/xfer9860.c Normal file
View File

@ -0,0 +1,154 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "xfer9860.h"
#include "common.h"
static char const error_notfound[] =
"Could not connect to the calculator.\n"
"- Is it plugged in and in receive mode?\n"
"- Have you tried changing the cable?\n";
/**
* Display device information.
*
* @param link Link to use to obtain the information.
* @return Cahute error.
*/
static int print_device_info(cahute_link *link) {
unsigned long capacity;
int err;
if ((err = cahute_request_storage_capacity(link, "fls0", &capacity)))
return err;
/* 1572864 is a special value taken from xfer9860.
* It may not be accurate for the device. */
printf(
"Storage memory: %lu%% (%luo) available.\n",
100 * capacity / 1572864,
capacity
);
return 0;
}
/**
* Main entry point to the program.
*
* @param argc Argument count.
* @param argv Argument values.
* @return Exit code for the program.
*/
int main(int argc, char **argv) {
struct args args;
cahute_link *link = NULL;
int bus, address, err = 0;
/* Since xfer9860 has no logging level of any kind, we disable logging
* entirely here. */
cahute_set_log_level(CAHUTE_LOGLEVEL_NONE);
if (!parse_args(argc, argv, &args))
return 0;
if ((err = find_usb_calculator(0, &bus, &address)))
goto fail;
if ((err = cahute_open_usb_link(&link, 0, bus, address)))
goto fail;
switch (args.operation) {
case OPERATION_UPLOAD:
err = cahute_send_file_to_storage(
link,
CAHUTE_SEND_FILE_FLAG_FORCE | CAHUTE_SEND_FILE_FLAG_OPTIMIZE,
NULL,
args.distant_target_name,
"fls0",
args.local_source_fp,
NULL,
NULL,
NULL,
NULL
);
break;
case OPERATION_DOWNLOAD:
err = cahute_request_file_from_storage(
link,
NULL,
args.distant_source_name,
"fls0",
args.local_target_fp,
NULL,
NULL
);
break;
case OPERATION_OPTIMIZE:
err = cahute_optimize_storage(link, "fls0");
break;
case OPERATION_INFO:
err = print_device_info(link);
break;
default:
err = CAHUTE_ERROR_IMPL;
break;
}
fail:
if (link)
cahute_close_link(link);
if (args.local_source_fp && args.local_source_path)
fclose(args.local_source_fp);
if (args.local_target_fp && args.local_target_path)
fclose(args.local_target_fp);
switch (err) {
case 0:
break;
case CAHUTE_ERROR_IMPL:
fprintf(stderr, "The operation was not implemented yet.\n");
break;
case CAHUTE_ERROR_TOO_MANY:
fprintf(stderr, "Too many found calculators, please only keep one.\n");
break;
case CAHUTE_ERROR_NOT_FOUND:
fprintf(stderr, error_notfound);
break;
default:
fprintf(stderr, "An unknown error has occurred.\n");
}
return 0;
}

72
cli/xfer9860.h Normal file
View File

@ -0,0 +1,72 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef XFER9860_H
#define XFER9860_H 1
#include <cahute.h>
#define OPERATION_UPLOAD 1
#define OPERATION_DOWNLOAD 2
#define OPERATION_INFO 3
#define OPERATION_OPTIMIZE 4
/**
* Parsed arguments structure.
*
* The operations are the following:
*
* - UPLOAD contents of a {local_source_fp} as {distant_target_name} on
* storage device "fls0".
* - DOWNLOAD contents of {distant_source_name} from storage device "fls0"
* into {local_target_fp}.
* - Get INFO regarding the calculator.
* - OPTIMIZE the "fls0" storage device.
*
* @property operation Selected operation.
* @property throttle Throttle time in seconds.
* @property distant_source_name Distant file name for download.
* @property distant_target_name Distant file name for upload.
* @property local_source_path Path of the local source file for upload.
* @property local_target_path Path of the local target file for download.
* @property local_source_fp Local source file pointer for upload.
* @property local_target_fp Local target file pointer for download.
*/
struct args {
int operation;
int throttle;
char const *distant_source_name;
char const *distant_target_name;
char const *local_source_path;
char const *local_target_path;
FILE *local_source_fp;
FILE *local_target_fp;
};
extern int parse_args(int argc, char **argv, struct args *args);
#endif /* XFER9860_H */

243
cli/xfer9860_args.c Normal file
View File

@ -0,0 +1,243 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#include "xfer9860.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char const about_message[] =
"xfer9860 - from Cahute v" CAHUTE_VERSION
" (licensed under CeCILL 2.1)\n"
"\n"
"This utility is a reimplementation of the utility originally made\n"
"by Andreas Bertheussen, Manuel Naranjo and Bruno L. Alata in 2007.\n"
"\n"
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or\n"
"FITNESS FOR A PARTICULAR PURPOSE.\n";
static char const help_message[] =
"Usage: %s [-h] [-a] [-t <throttle>] ...\n"
"fx-9860G (SD) communication utility.\n"
"\n"
"Usage:\n"
" xfer9860 -u <local file path> <file name>\n"
" Upload the file as <file name> on the calculator's main\n"
" storage device.\n"
"\n"
" xfer9860 -d <file name> <local file path>\n"
" Download the file named <file name> from the calculator's\n"
" main storage device.\n"
"\n"
" xfer9860 -i\n"
" Show information about the connected calculator.\n"
"\n"
" xfer9860 -o\n"
" Optimize the calculator's main storage device.\n"
"\n"
"Available options are:\n"
" -h Show this help message and exit.\n"
" -a Show the about message and exit.\n"
" -t <throttle> Select the throttle in seconds, i.e. maximum\n"
" delay between two packets.\n"
"\n"
"For guides, topics and reference, consult the documentation:\n"
" " CAHUTE_URL
"\n"
"\n"
"For reporting issues and vulnerabilities, consult the following guide:\n"
" " CAHUTE_ISSUES_URL "\n";
/**
* Short options for getopt().
*/
static char const *short_options = "hat:u:d:io";
/**
* Parse the command-line parameters into a parsed arguments structure.
*
* @param argc Argument count.
* @param argv Argument values.
* @param args Parsed arguments structure to define.
* @return 1 if the parameters have been parsed successfully, 0 otherwise.
*/
int parse_args(int argc, char **argv, struct args *args) {
char const *command_path = argv[0];
int about = 0, help = 0, multiple_operations = 0;
args->operation = 0;
args->throttle = 0;
args->distant_source_name = NULL;
args->distant_target_name = NULL;
args->local_source_path = NULL;
args->local_target_path = NULL;
args->local_source_fp = NULL;
args->local_target_fp = NULL;
opterr = 0;
while (1) {
int option = getopt(argc, argv, short_options);
if (option < 0)
break;
switch (option) {
case 't':
/* Original command used 'atoi()' here. */
args->throttle = atoi(optarg);
break;
case 'a':
about = 1;
break;
case 'h':
help = 1;
break;
case 'u':
/* Upload mode! */
if (args->operation)
multiple_operations = 1;
args->operation = OPERATION_UPLOAD;
args->distant_target_name = optarg;
break;
case 'd':
/* Download mode! */
if (args->operation)
multiple_operations = 1;
args->operation = OPERATION_DOWNLOAD;
args->distant_source_name = optarg;
break;
case 'o':
/* Optimize mode! */
if (args->operation)
multiple_operations = 1;
args->operation = OPERATION_OPTIMIZE;
break;
case 'i':
/* Information mode! */
if (args->operation)
multiple_operations = 1;
args->operation = OPERATION_INFO;
break;
default:
/* xfer9860 abandons when it receives an unknown option.
* We want to display the help message! */
help = 1;
goto process_params;
}
}
process_params:
argc -= optind;
argv += optind;
if (about) {
fprintf(stderr, about_message);
return 0;
}
if (multiple_operations || !args->operation)
help = 1;
else if (args->operation == OPERATION_UPLOAD) {
if (argc != 1)
help = 1;
else
args->local_source_path = argv[0];
} else if (args->operation == OPERATION_DOWNLOAD) {
if (argc != 1)
help = 1;
else
args->local_target_path = argv[0];
} else {
if (argc != 0)
help = 1;
}
if (help) {
fprintf(stderr, help_message, command_path);
return 0;
}
if (args->distant_target_name
&& strnlen(args->distant_target_name, 13) > 12) {
fprintf(
stderr,
"The destination filename is too long: %s\n"
"Filesystem only supports 12 characters.",
args->distant_target_name
);
return 0;
}
if (args->distant_source_name
&& strnlen(args->distant_source_name, 13) > 12) {
fprintf(
stderr,
"The source filename is too long: %s\n"
"Filesystem only supports 12 characters.",
args->distant_source_name
);
return 0;
}
if (args->local_source_path) {
args->local_source_fp = fopen(args->local_source_path, "rb");
if (!args->local_source_fp) {
fprintf(
stderr,
"Unable to open file: %s\n",
args->local_source_path
);
return 0;
}
}
if (args->local_target_path) {
args->local_target_fp = fopen(args->local_target_path, "w");
if (!args->local_target_fp) {
fprintf(
stderr,
"Unable to open file: %s\n",
args->local_target_path
);
return 0;
}
}
return 1;
}

View File

@ -0,0 +1,249 @@
# *****************************************************************************
# Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
#
# This software is governed by the CeCILL 2.1 license under French law and
# abiding by the rules of distribution of free software. You can use, modify
# and/or redistribute the software under the terms of the CeCILL 2.1 license
# as circulated by CEA, CNRS and INRIA at the following
# URL: https://cecill.info
#
# As a counterpart to the access to the source code and rights to copy, modify
# and redistribute granted by the license, users are provided only with a
# limited warranty and the software's author, the holder of the economic
# rights, and the successive licensors have only limited liability.
#
# In this respect, the user's attention is drawn to the risks associated with
# loading, using, modifying and/or developing or reproducing the software by
# the user in light of its specific status of free software, that may mean
# that it is complicated to manipulate, and that also therefore means that it
# is reserved for developers and experienced professionals having in-depth
# computer knowledge. Users are therefore encouraged to load and test the
# software's suitability as regards their requirements in conditions enabling
# the security of their systems and/or data to be ensured and, more generally,
# to use and operate it in the same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL 2.1 license and that you accept its terms.
# *****************************************************************************
"""Sphinx plugin to add the "seven-command" reStructuredText directive."""
from __future__ import annotations
import re
from typing import TYPE_CHECKING
import docutils.nodes as nodes
from docutils.parsers.rst import Directive
from docutils.parsers.rst.directives import unchanged
from docutils.statemachine import StringList
if TYPE_CHECKING:
from sphinx.application import Sphinx
def ascii_hex(
*,
size: int,
default: int | None = None,
) -> Callable[[str | None], int]:
"""Convert the argument into an ASCII-HEX number.
:param size: Size of the expected ASCII-HEX.
"""
if default is not None and (default < 0 or default >= 2 ** size):
raise ValueError(f"default must be between 0 and {2 ** size - 1}")
def func(argument: str | None) -> int:
"""Convert the argument into a short hexadecimal number."""
if argument is None:
if default is None:
raise ValueError("missing value")
return default
m = re.fullmatch(
r'[0-9A-Za-z]{' + re.escape(str(size)) + '}',
argument,
)
if m is None:
raise ValueError(f"expected a {size}-digit hex")
return int(m.group().upper(), 16)
return func
class SevenCommandDirective(Directive):
"""Directive for documenting a Protocol 7.00 command."""
has_content = True
option_spec = {
"code": ascii_hex(size=2),
"ow": unchanged,
"ow-example": ascii_hex(size=2, default=0),
"dt": unchanged,
"dt-example": ascii_hex(size=2, default=0),
"fs": unchanged,
"fs-example": ascii_hex(size=8, default=0),
"d1": unchanged,
"d1-example": unchanged,
"d2": unchanged,
"d2-example": unchanged,
"d3": unchanged,
"d3-example": unchanged,
"d4": unchanged,
"d4-example": unchanged,
"d5": unchanged,
"d5-example": unchanged,
"d6": unchanged,
"d6-example": unchanged,
}
def is_payload_required(self) -> bool:
"""Whether at least one field is used for the command."""
return bool(
self.options.get("ow")
or self.options.get("dt")
or self.options.get("fs")
or self.options.get("d1")
or self.options.get("d2")
or self.options.get("d3")
or self.options.get("d4")
or self.options.get("d5")
or self.options.get("d6")
)
def get_table(self) -> nodes.table:
"""Get the table."""
table = nodes.table()
table['classes'] += ['colwidths-auto']
tgroup = nodes.tgroup(cols=2)
table += tgroup
tbody = nodes.tbody()
tgroup += nodes.colspec()
tgroup += nodes.colspec()
def add_entry(name: str, description: str, /) -> None:
"""Add an entry to the table."""
nonlocal tbody
row_node = nodes.row()
tbody += row_node
title_entry_node = nodes.entry()
desc_entry_node = nodes.entry()
row_node += title_entry_node
row_node += desc_entry_node
title = nodes.strong()
title += [nodes.Text(name)]
title_entry_node += [title]
self.state.nested_parse(
StringList([description]),
self.content_offset,
desc_entry_node
)
if self.options.get("ow"):
add_entry("Overwrite (OW)", self.options["ow"])
if self.options.get("dt"):
add_entry("Data Type (DT)", self.options["dt"])
if self.options.get("fs"):
add_entry("Filesize (FS)", self.options["fs"])
for k in range(1, 7):
if self.options.get(f"d{k}"):
add_entry(f"Data {k} (D{k})", self.options[f"d{k}"])
tgroup += tbody
return table
def get_example(self) -> str:
"""Get an example to display."""
code = self.options["code"]
ow = self.options.get("ow-example") or 0
dt = self.options.get("dt-example") or 0
fs = self.options.get("fs-example") or 0
d1 = self.options.get("d1-example") or ""
d2 = self.options.get("d2-example") or ""
d3 = self.options.get("d3-example") or ""
d4 = self.options.get("d4-example") or ""
d5 = self.options.get("d5-example") or ""
d6 = self.options.get("d6-example") or ""
header = "T ST "
content = f". {code:02X} "
if self.is_payload_required():
# Compute the unordered string to compute the checksum and the
# size of the data.
raw_unordered = (
f"{ow:02X}{dt:02X}{fs:08X}"
+ "".join(f"{len(x):02X}{x}" for x in (d1, d2, d3, d4, d5, d6))
)
data_size = len(raw_unordered)
raw_unordered += f"{code:02X}1{data_size:04X}"
checksum = (~sum(raw_unordered.encode("ascii")) + 1) & 255
header += "EX DS OW DT FS "
header += " ".join(f"SD{i}" for i in range(1, 7))
content += f" 1 {data_size:04X} {ow:02X} {dt:02X} {fs:08X} "
content += " ".join(f" {len(x):02X}" for x in (d1, d2, d3, d4, d5, d6))
for i, x in enumerate((d1, d2, d3, d4, d5, d6)):
if not x:
continue
# We need to align the string and the header, even if the string
# is only 1 character long.
if len(x) == 1:
x = " " + x
header += f" D{i + 1}" + " " * ((len(x) - 2))
content += f" {x}"
else:
header += "EX "
content += " 0 "
checksum = (~sum(f"{code:02X}0".encode("ascii")) + 1) & 255
header += " CS"
content += f" {checksum:02X}"
return header + "\n" + content
def run(self) -> list[nodes.Node]:
"""Run the directive.
:return: Produced nodes.
"""
container = nodes.container()
if self.is_payload_required():
container += self.get_table()
if self.content:
self.state.nested_parse(self.content, self.content_offset, container)
paragraph_node = nodes.paragraph()
container += paragraph_node
paragraph_node += [
nodes.Text("An example of such command is the following:"),
]
example_node = nodes.literal_block()
example_node["language"] = "text"
container += example_node
example_node += [nodes.Text(self.get_example())]
return [container]
def setup(app: Sphinx, /) -> None:
"""Set up the extension.
:param app: The Sphinx application to set up the extension for.
"""
app.add_directive("seven-command", SevenCommandDirective)

14
docs/_static/cahute.svg vendored Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80" height="80" version="1.1" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(210 -414.59)">
<path class="st0" d="m-143.25 487.96c-37.376-45.16-18.688-22.58 0 0zm-17.694-49.046c3.1042 0.31042 3.725 1.8625 4.0354 3.4146 0.62083 2.4834 0.62083 20.177 0 23.281-0.31042 2.1729-1.8625 3.725-3.725 4.0354-0.93125 0-2.4833 0.31043-3.1042 0.31043v17.694c1.2417 0 4.3458 0 7.1396-0.31042 6.5187-0.93125 11.796-4.6562 13.658-12.727 1.2417-5.5875 1.2417-32.594 0-38.802-1.5521-6.5188-5.5875-12.106-12.417-13.658-6.2083-1.2417-22.35-1.2417-29.179 0-4.9667 0.93125-10.865 5.5875-12.417 13.348-1.2417 7.1396-1.5521 32.283 0 39.112 2.1729 9.0021 10.244 11.796 13.038 12.106 3.1042 0.31042 5.2771 0.31042 6.5188 0.31042v-17.694c-1.2417 0-4.6562 0.31042-5.5875-2.7938-0.62083-1.8625-0.62083-19.246-0.31042-23.592 0.31042-2.1729 1.2417-3.4146 3.4146-3.725 4.3458-0.93125 14.9-0.93125 18.935-0.31042z" fill="#003296"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

19
docs/_static/custom.css vendored Normal file
View File

@ -0,0 +1,19 @@
h1 img, h2 img, p img {
vertical-align: middle;
width: 1em;
}
.sidebar-logo {
width: 80%;
}
.mermaid {
text-align: center;
}
.bottom-of-page .icons svg,
.bottom-of-page .icons img {
width: auto;
height: 2em;
vertical-align: middle;
}

BIN
docs/_static/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
docs/_static/favicon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

1
docs/_static/made_by_a_human.svg vendored Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="88" height="31" viewBox="0 0 88 31"><g id="Background"><rect width="88" height="31" fill="#1565c0"/></g><g id="_3D"><polygon points="88 0 88 31 0 31 2 29 86 29 86 2 88 0" fill="#263238" opacity=".5"/><polygon points="88 0 86 2 2 2 2 29 0 31 0 0 88 0" fill="#fff" opacity=".5"/></g><g id="Text"><path d="m40.86,9.65c0-1.08-.59-1.64-1.48-1.64s-1.49.56-1.49,1.64v3.31h-1.21v-3.31c0-1.08-.59-1.64-1.48-1.64s-1.49.56-1.49,1.64v3.31h-1.22v-5.92h1.22v.68c.4-.48,1.04-.77,1.75-.77.93,0,1.72.4,2.13,1.17.37-.72,1.2-1.17,2.06-1.17,1.39,0,2.44.87,2.44,2.52v3.49h-1.21v-3.31Z" fill="#fff"/><path d="m46.01,6.94c1,0,1.69.47,2.05.96v-.86h1.24v5.92h-1.24v-.88c-.38.5-1.08.98-2.07.98-1.54,0-2.77-1.26-2.77-3.08s1.24-3.03,2.79-3.03Zm.26,1.06c-.91,0-1.79.69-1.79,1.97s.88,2.02,1.79,2.02,1.79-.72,1.79-2-.87-1.99-1.79-1.99Z" fill="#fff"/><path d="m53.3,6.94c.77,0,1.59.37,2.04.92v-2.86h1.24v7.95h-1.24v-.89c-.38.54-1.08.99-2.05.99-1.56,0-2.79-1.26-2.79-3.08s1.24-3.03,2.8-3.03Zm.25,1.06c-.91,0-1.79.69-1.79,1.97s.88,2.02,1.79,2.02,1.79-.72,1.79-2-.87-1.99-1.79-1.99Z" fill="#fff"/><path d="m60.73,13.05c-1.7,0-2.95-1.2-2.95-3.06s1.2-3.05,2.95-3.05,2.88,1.17,2.88,2.91c0,.2-.01.4-.04.6h-4.52c.09.98.78,1.57,1.69,1.57.75,0,1.17-.37,1.4-.83h1.32c-.33,1.03-1.27,1.86-2.72,1.86Zm-1.68-3.59h3.28c-.02-.91-.74-1.49-1.65-1.49-.83,0-1.49.56-1.62,1.49Z" fill="#fff"/><path d="m70.89,6.94c1.57,0,2.78,1.2,2.78,3.03s-1.22,3.08-2.78,3.08c-.98,0-1.68-.44-2.06-.96v.86h-1.22v-7.95h1.22v2.91c.39-.54,1.13-.98,2.06-.98Zm-.27,1.06c-.91,0-1.79.72-1.79,1.99s.88,2,1.79,2,1.8-.74,1.8-2.02-.88-1.97-1.8-1.97Z" fill="#fff"/><path d="m78.95,7.04h1.27l-3.63,8.7h-1.27l1.2-2.88-2.33-5.82h1.36l1.67,4.51,1.73-4.51Z" fill="#fff"/><path d="m34.48,17.94c1,0,1.69.47,2.05.96v-.86h1.24v5.92h-1.24v-.88c-.38.5-1.08.98-2.07.98-1.54,0-2.77-1.26-2.77-3.08s1.24-3.03,2.79-3.03Zm.26,1.06c-.91,0-1.79.69-1.79,1.97s.88,2.02,1.79,2.02,1.79-.72,1.79-2-.87-1.99-1.79-1.99Z" fill="#fff"/><path d="m42.17,16.01h1.22v2.72c.41-.49,1.07-.78,1.84-.78,1.32,0,2.35.87,2.35,2.52v3.49h-1.21v-3.31c0-1.08-.59-1.64-1.48-1.64s-1.49.56-1.49,1.64v3.31h-1.22v-7.95Z" fill="#fff"/><path d="m54.5,23.96h-1.22v-.71c-.39.5-1.05.79-1.75.79-1.39,0-2.44-.87-2.44-2.52v-3.48h1.21v3.3c0,1.08.59,1.64,1.48,1.64s1.49-.56,1.49-1.64v-3.3h1.22v5.92Z" fill="#fff"/><path d="m64.5,20.65c0-1.08-.59-1.64-1.48-1.64s-1.49.56-1.49,1.64v3.31h-1.21v-3.31c0-1.08-.59-1.64-1.48-1.64s-1.49.56-1.49,1.64v3.31h-1.22v-5.92h1.22v.68c.4-.48,1.04-.77,1.75-.77.93,0,1.72.4,2.13,1.17.37-.72,1.2-1.17,2.06-1.17,1.39,0,2.44.87,2.44,2.52v3.49h-1.21v-3.31Z" fill="#fff"/><path d="m69.65,17.94c1,0,1.69.47,2.05.96v-.86h1.24v5.92h-1.24v-.88c-.38.5-1.08.98-2.07.98-1.54,0-2.77-1.26-2.77-3.08s1.24-3.03,2.79-3.03Zm.26,1.06c-.91,0-1.79.69-1.79,1.97s.88,2.02,1.79,2.02,1.79-.72,1.79-2-.87-1.99-1.79-1.99Z" fill="#fff"/><path d="m78.74,20.65c0-1.08-.59-1.64-1.48-1.64s-1.49.56-1.49,1.64v3.31h-1.22v-5.92h1.22v.68c.4-.48,1.05-.77,1.76-.77,1.39,0,2.43.87,2.43,2.52v3.49h-1.21v-3.31Z" fill="#fff"/></g><g id="Image"><circle cx="15.37" cy="15.5" r="10.3" fill="#ffcc80" stroke="#263238" stroke-miterlimit="10" stroke-width="1.5"/><rect x="7.55" y="13.8" width="2.54" height="3.4" rx="1.27" ry="1.27" fill="#263238"/><rect x="20.65" y="13.8" width="2.54" height="3.4" rx="1.27" ry="1.27" fill="#263238"/><path d="m18.44,15.5c0,1.7-1.37,3.07-3.07,3.07s-3.07-1.37-3.07-3.07" fill="none" stroke="#263238" stroke-miterlimit="10" stroke-width="1.5"/></g></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

240
docs/_static/planete_casio.svg vendored Normal file
View File

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="76.994mm" height="17.992mm" version="1.1" viewBox="0 0 76.994 17.992" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image width="76.994" height="17.992" preserveAspectRatio="none" xlink:href="data:image/png;base64,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
WS3GlOhwnHb1bj90KOwPhMzm7v5+t9stPkk5LIQjRviusrhGdjU0ZI+NLcrIGBscHENMqbgfbbph
QqWlaUNDZgbJ3hmOY0CJTaRtxxjZj0hOZCBrFonICD5XphlfwkCQTC6IxJBCE4otNTX/gQeiiYmk
ksXIDAnGat3YoPQlF7MqK+e//XZKYSGQ4rJMUE5F4rN40SJqw5o17iNHDAErEsZRUTE4OCj3UwS8
1uxs55o1oN3hlSshtogas2a33/HNb971zDOGLsSn7GECP+xFRdPXr8+vrT354ovhvj5Da6XQOLoD
dmnMqDab0QjIxcGsxWq98/nnQdP4BjVVYBaZ1dXp5eWTKyrOvv46h3wyzesa8gTexIQlviJTiyix
nHvPPXe98ELB7NmsLOEcVHIBiFmV1DvumL5hQ8fmzf379wNh0eGddXUzvvENx7RpwhooYonwFO6r
ilWrcmbObPrhDz2nTws3TMrJmfvznxfOmQONyTRlMQXmbNG686c/bcvKuvrBB6BoPG9gkrQL+xBl
9eFf/rJk4UKzyqQVvi8Lga3Ogt7x0kv5s2cfX7eOPEGJ69DSLb/VSf5EHpPOfEjO6OoiBJzqdA6d
PZuQqKkt1hr/qBqD08wMUQHKkpCYONTX592yxf7Iwsx5D0UC/qA6thFqAbLMWVdCCSCxnexqcKTX
HwAYA729Ax5PaXHxw/fd55wyBT9kXmamw2ZzBwItXV0HT5zo7OychPMDsa7rPI6iBqWJ6eXlT2Vl
GeSpXLwQzMgYTUtPUdGC0JUrI243bBIDF07zn4zS+OszvvMd6qT/q+AWHbh6dbixEZo3ErKp0qBR
RT+Zvnq1NTe3a9eu9vp6AifCa5gCcEBulK1bV/yVrxQsW4buzUXutyjkDgwPX/jd7zjviEYlExU1
mJC9OSOjcuPGpIKCgiVLrm7fHoEbKhlVvWrV6ODguTfeuLZrlwbxU3DqUDUtY+ZM58svj2VmZs2Z
4/7b36LqFImOd9/tev/9wmXL8p56yn34cPDAAdpH+6A1SlpJSekjj6BQna+v569cZ440CM8CDXIX
LJi2dm1CURFehMEjRzDp7Xl5d61f71y9+nOt/uHOzgubNvXs3q2rlPT8u+/OnTmzt6Gh+4MPdCjf
bJaj4KM0TqTn/vvtc+akzp3LDsahU6cI8DgKC0sWLbLl5ra99VbXjh3Ee8SKxojA3eWYObPqxRcd
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
kf8AIqUGvdlPCQsAAAAASUVORK5CYII=
"/></svg>

After

Width:  |  Height:  |  Size: 18 KiB

53
docs/acknowledgements.rst Normal file
View File

@ -0,0 +1,53 @@
Acknowledgements
================
There have been many projects over the years about reversing and
reimplementing CASIO's shenannigans for using their calculators from
alternative OSes, or simply for fun or out of curiosity. Cahute couldn't
have been made without their research, implementations and in some cases,
documentation. This page is a little tribute to these works.
* Thanks to Tom Wheeley and Tom Lynn for their work on CaS and Caspro_
and the `Casio Graphical Calculator Encyclopaedia`_.
* Thanks to the (now defunct) Graph100.com wiki, `saved here
<Graph100.com Wiki_>`_ for historical purposes.
* Thanks to the team behind Casetta_ for their documentation on legacy
protocols and file formats, which helped me navigate the subtleties more
easily.
* Thanks to `Simon Lothar`_ and Andreas Bertheussen for their work on
Protocol 7.00 and derivatives through fxReverse_ and xfer9860, and to
Teamfx_ for `their additions <Teamfx additions_>`_.
* Thanks to the Cemetech community for their `Prizm Wiki`_, especially
gbl08ma, BrandonWilson and amazonka.
* Thanks to Nessotrin_ for their work on UsbConnector_, which prompted me
to work on a better version in the first place.
There are obviously plenty more people working on other connected aspects
(hardware, low-level system stuff), administering or moderating forums and
websites, maintaining communication with CASIO and other partners.
Quoting you all would take a substantial time, and I'd likely miss quite a lot
of you, but thank you all for your efforts!
.. _Simon Lothar:
https://www.casiopeia.net/forum/memberlist.php?mode=viewprofile&u=10405
.. _Teamfx:
https://www.casiopeia.net/forum/memberlist.php?mode=viewprofile&u=10504&sid=b1f4fb842b29e6f686d832a7e1117789
.. _Nessotrin:
https://www.planet-casio.com/Fr/compte/voir_profil.php?membre=nessotrin
.. _Casio Graphical Calculator Encyclopaedia:
https://serval.mythic-beasts.com/~tom/calcs/calcs/encyc/
.. _Graph100.com Wiki:
https://bible.planet-casio.com/cakeisalie5/websaves/graph100.com/
.. _fxReverse:
https://bible.planet-casio.com/simlo/fxreverse/fxReverse2.pdf
.. _Teamfx additions: https://bible.planet-casio.com/teamfx/
.. _Prizm Wiki: https://prizm.cemetech.net/
.. _UsbConnector:
https://www.planet-casio.com/Fr/forums/topic13656-1-usbconnector
-remplacement-de-fa124-multi-os.html
.. _Casetta: https://casetta.tuxfamily.org/
.. _Caspro:
https://web.archive.org/web/20160504230033/
http://www.spiderpixel.co.uk:80/caspro/

12
docs/cli.rst Normal file
View File

@ -0,0 +1,12 @@
Command line reference
======================
This section presents the command line reference, i.e. all options and
syntaxes support by Cahute's command-line utilities.
.. toctree::
:maxdepth: 1
cli/p7
cli/p7screen
cli/xfer9860

391
docs/cli/p7.rst Normal file
View File

@ -0,0 +1,391 @@
.. _p7:
``p7`` command line reference
=============================
p7 is a utility originally provided with libp7_ back in August of 2016 by
`Thomas Touhey`_. It is used to interact with the calculator's storage
devices from any calculator using Protocol 7.00.
.. warning::
This interface is provided by compatibility with libp7 / libcasio, and
must not be changed to bring new features or change the syntax of
existing ones.
For concrete steps in using p7, consult the following guides:
* :ref:`guide-cli-send-file`;
* :ref:`guide-cli-get-file`;
* :ref:`guide-cli-get-info`.
Available options for all subcommands are the following:
``-l``, ``--log``
Logging level to set the library as, as any of ``info``, ``warning``,
``error``, ``fatal``, ``none``.
This option may allow contributors to visualize the effects of their
contribution better, and allows users to produce a full output to join
to an issue.
See :ref:`logging` for more information.
p7 is used through subcommands that work on a link, with the exception of
the ``list-devices`` subcommand. Available options for such subcommands
are the following:
``--com <device>``
Path or name of the serial device with which to communicate,
e.g. ``/dev/ttyUSB0``.
If this option is not provided, p7 will look for a calculator connected
through USB.
``--use <settings>``
Serial settings to instantiate the serial link with.
This parameter is of the following format:
.. code-block:: text
<Speed in bauds><Parity><Stop bits>
Where:
* The speed in bauds is expressed as an integer, e.g. ``19200``.
* The parity is either ``O`` for odd parity, ``E`` for even parity,
and ``N`` if parity checks are disabled altogether.
* The stop bits is either ``1`` or ``2``.
By default, the serial link is instanciated with a speed of 9600 bauds,
no parity and 2 stop bits (``9600N2``).
This option is ignored if the path or name of a serial device is not
provided using the ``--com`` option.
``--set <settings>``
Serial settings to negotiate with the calculator.
The parameter is of the same format as the ``--use`` option described
above.
This option is ignored if the path or name of a serial device is not
provided using the ``--com`` option.
``--reset``
Short hand form of ``--set 9600N2``.
``--no-init``
Disable the initiation handshake when the link is opened.
This is mostly useful if combining multiple consecutive p7 subcommands,
if the previous one was passed ``--no-exit``.
``--no-exit``
Disable the termination handshake when the link is closed.
This is mostly useful if combining multiple consecutive p7 subcommands,
provided the next one is passed ``--no-init``.
Invalid options are ignored by p7. If an option is provided several time,
only the latest occurrence will be taken into account.
See the following sections for subcommand specification.
.. _p7-list-devices:
``list-devices`` subcommand reference
-------------------------------------
This subcommand is used to list serial devices available to be used by p7.
This subcommand does not provide additional options.
An example execution is the following:
.. code-block:: text
$ p7 list-devices
Available devices:
- /dev/ttyUSB1
- /dev/ttyUSB2
.. _p7-info:
``info`` subcommand reference
-----------------------------
This subcommand is used to get information regarding a calculator.
The syntax is the following:
.. code-block:: text
p7 info
.. _p7-idle:
``idle`` subcommand reference
-----------------------------
This subcommand does not run any use case, and is used to only initiate,
terminate, and/or update serial parameters on a link, when chaining multiple
commands.
The syntax is the following:
.. code-block:: text
p7 idle
.. _p7-send:
``send`` subcommand reference
-----------------------------
This subcommand is used for sending a file to a storage device on the
calculator.
The syntax is the following:
.. code-block:: text
p7 send [options...] <local file path>
Where the local file path is the path to the local file to send, relative
to the working directory; for example, ``myaddin.g1a``.
Available options are the following:
``-#``
Flag to enable displaying of a loading bar to show transfer progress.
``-f``, ``--force``
If the file already exists in the calculator's device storage, force
overwriting it or not.
By default, if the case occurs, the subcommand requests an interactive
overwrite confirmation or rejection.
``-o``, ``--output``
File name the file should be stored as on the storage device,
e.g. ``-o MYADDIN.G1A``.
By default, the output file name is determined using the base name
of the local path.
``-d``, ``--directory``
Directory name in which the file should be sent to.
By default, the file is sent to the storage device's root.
``--storage``
Name of the storage device on which the file should be stored.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _p7-get:
``get`` subcommand reference
----------------------------
This command is used to request a file from a storage device on the
calculator.
The syntax is the following:
.. code-block:: text
p7 get [options...] <distant file name>
Where the distant file name is the name of the file on the calculator.
.. warning::
Note that the parameter is **NOT** the path of the file, but the name
of the file. If you wish to request a file from a directory, you must
use the ``-d`` or ``--directory`` option.
Available options are the following:
``-#``
Flag to enable displaying of a loading bar to show transfer progress.
``-o``, ``--output``
Local path of the file to which to write the result, absolute or relative
to the working directory.
If the file already exists, it will be overwritten automatically.
By default, this is set to the distant file name in the current working
directory.
``-d``, ``--directory``
Name of the distant directory in which the requested file is present.
By default, the file is requested from the storage device's root.
``--storage``
Name of the storage device from which the file should be requested.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _p7-copy:
``copy`` subcommand reference
-----------------------------
This subcommand is used to copy a distant file to another on a storage device
on the calculator.
The syntax is the following:
.. code-block:: text
p7 copy [options...] <source file name> <target file name>
.. warning::
Both parameters are **NOT** the file paths, but the file names.
If you wish to set a directory for the source file, use the ``-d``
or ``--directory`` option.
If you wish to set a directory for the target file, use the ``-t``
or ``--to`` option.
Available options are the following:
``-d``, ``--directory``
Directory in which the source file should be retrieved from.
By default, the source file is retrieved from the storage device's root.
``-t``, ``--to``
Directory in which the target file should be created in.
By default, the target file is created in the storage device's root.
``--storage``
Name of the storage device on which the copy should occur.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _p7-delete:
``delete`` subcommand reference
-------------------------------
This subcommand is used to delete a distant file on a storage device on
the calculator.
The syntax is the following:
.. code-block:: text
p7 delete [options...] <distant file name>
.. warning::
Note that the parameter is **NOT** the path of the file, but the name
of the file. If you wish to request a file from a directory, you must
use the ``-d`` or ``--directory`` option.
Available options are the following:
``-d``, ``--directory``
Directory in which the file should be deleted from.
By default, the file is deleted from the storage device's root.
``--storage``
Name of the storage device on which the deletion should occur.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _p7-list:
``list`` subcommand reference
-----------------------------
This subcommand is used to list files present on a storage device on
the calculator.
The syntax is the following:
.. code-block:: text
p7 list [options...]
Available options are the following:
``-d``, ``--directory``
Directory in which the file should be listed in.
By default, this subcommand lists files from all directories, including
the storage device's root.
``--storage``
Name of the storage device on which to list files.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _p7-reset:
``reset`` subcommand reference
------------------------------
This subcommand is used to reset a storage device on the calculator.
The syntax is the following:
.. code-block:: text
p7 reset [options...]
Available options are the following:
``--storage``
Name of the storage device to reset.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _p7-optimize:
``optimize`` subcommand reference
---------------------------------
This subcommand is used to optimize a storage device on the calculator,
i.e. defragment in order to save space.
The syntax is the following:
.. code-block:: text
p7 optimize [options...]
Available options are the following:
``--storage``
Name of the storage device to optimize.
By default, this is set to ``fls0`` (flash memory filesystem).
However, this can be set to other storage device names, such as
``crd0`` (SD card) for calculators with an SD card slot.
.. _libp7: https://p7.planet-casio.com/
.. _Thomas Touhey: https://thomas.touhey.fr/

42
docs/cli/p7screen.rst Normal file
View File

@ -0,0 +1,42 @@
.. _p7screen:
``p7screen`` command line reference
===================================
p7screen is a utility originally provided with libp7_ back in
September of 2016 by `Thomas Touhey`_. It is used for receiving the
screen from any USB calculator in "Projector", "Screen Capture" or
"Screen Receiver" mode, and displaying it to a basic window using SDL_.
.. warning::
This interface is provided by compatibility with libp7 / libcasio, and
must not be changed to bring new features or change the syntax of existing
ones.
For concrete steps on using p7screen, see :ref:`guide-cli-display-screen`.
Available options are the following:
``-z``, ``--zoom``
Zoom, as an integer from 1 to 16.
Having a zoom of N means that a single pixel on the calculator
will be displayed as an NxN full square on the host.
``-l``, ``--log``
Logging level to set the library as, as any of ``info``, ``warning``,
``error``, ``fatal``, ``none``.
This option may allow contributors to visualize the effects of their
contribution better, and allows users to produce a full output to join
to an issue.
See :ref:`logging` for more information.
Invalid options are ignored by p7screen. If an option is provided several time,
only the latest occurrence will be taken into account.
.. _libp7: https://p7.planet-casio.com/
.. _Thomas Touhey: https://thomas.touhey.fr/
.. _SDL: https://www.libsdl.org/

47
docs/cli/xfer9860.rst Normal file
View File

@ -0,0 +1,47 @@
.. _xfer9860:
``xfer9860`` command line reference
===================================
`xfer9860 <xfer9860 project page_>`_ is a tool originally released back in
June 2007 by Andreas Bertheussen for their fx-9860G SD. It is used to interact
with the calculator's storage memory from any calculator supporting
Protocol 7.00.
.. warning::
This interface is provided by compatibility with Andres Bertheussen's
work, and must not be changed to bring new features or change the syntax of
existing ones.
The utility operates differently depending on the provided option:
``xfer9860 -u <local file path> <file name>``
**U**\ pload the file as ``<file name>`` on the calculator's main
storage device.
``xfer9860 -d <file name> <local file path>``
**D**\ ownload the file named ``<file name>`` on the calculator's main
storage device.
``xfer9860 -i``
Gather **i**\ nformation regarding the calculator.
``xfer9860 -o``
**O**\ ptimize the calculator's main storage device.
Available options for all operations are the following:
``-h``, ``-a``
Display the help message and exit.
``-a`` actually stands for "**a**\ bout", and only displayed licensing
information while ``-h`` stands for "**h**\ elp" and only displayed
usage help.
Cahute's implementation shows the same message for both options.
``-t``
**T**\ hrottle in seconds, i.e. delay in milliseconds between packets.
.. _xfer9860 project page: https://sourceforge.net/projects/xfer9860/

118
docs/conf.py Normal file
View File

@ -0,0 +1,118 @@
"""Configuration file for the Sphinx documentation builder.
For the full list of built-in configuration values, see the documentation:
https://www.sphinx-doc.org/en/master/usage/configuration.html
"""
from __future__ import annotations
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent / "_ext"))
project = "Cahute"
version = "0.1"
copyright = "2024, Thomas Touhey"
author = "Thomas Touhey"
extensions = [
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinxcontrib.mermaid",
"add_seven_command_directive",
]
templates_path: list[str] = []
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
primary_domain = "c"
html_theme = "furo"
html_theme_options = {
"footer_icons": [
{
"name": "Planète Casio",
"url": "https://www.planet-casio.com/Fr/",
"html": (Path("_static") / "planete_casio.svg").open().read(),
"class": ""
},
{
"name": "Made by a Human",
"url": "/project.html",
"html": (Path("_static") / "made_by_a_human.svg").open().read(),
"class": "",
},
],
}
html_static_path = ["_static"]
html_title = f"Cahute {version}"
html_favicon = '_static/favicon.png'
html_logo = '_static/cahute.svg'
html_use_index = False
html_copy_source = False
html_show_sourcelink = False
html_domain_indices = False
html_css_files = ["custom.css"]
intersphinx_mapping = {}
todo_include_todos = True
mermaid_output_format = "raw"
mermaid_init_js = """
function isDarkMode() {
const color = (
getComputedStyle(document.body)
.getPropertyValue("--color-foreground-primary")
);
if (color == "#ffffffcc")
return true;
return false;
}
function initializeMermaid(isStart) {
mermaid.initialize({
startOnLoad: isStart,
theme: isDarkMode() ? "dark" : "base",
darkMode: isDarkMode(),
securityLevel: "antiscript"
});
}
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (
mutation.type != "attributes"
|| mutation.attributeName != "data-theme"
)
return
const nodes = document.querySelectorAll(".mermaid");
nodes.forEach(node => {
/* Restore the original code before reprocessing. */
node.innerHTML = node.getAttribute("data-original-code");
/* Remove the attribute saying data is processed; it is not! */
if (node.hasAttribute("data-processed"))
node.removeAttribute("data-processed");
});
initializeMermaid(false);
mermaid.run({nodes: nodes, querySelector: ".mermaid"});
});
});
(function (window) {
/* Store original code for diagrams into an attribute directly, since
Mermaid actually completely replaces the content and removes the
original code. */
document.querySelectorAll(".mermaid").forEach(node => {
node.setAttribute("data-original-code", node.innerHTML);
})
initializeMermaid(true);
observer.observe(document.body, {attributes: true});
})(window);
"""

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
docs/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

14
docs/guides.rst Normal file
View File

@ -0,0 +1,14 @@
Guides
======
This section consists of multiple guides for solving specific problems.
.. toctree::
:maxdepth: 2
guides/install
guides/cli
guides/developer
guides/report
guides/package
guides/contribute

8
docs/guides/arch.svg Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="211.66" height="211.66" version="1.0" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-34.777,-129.8)">
<g transform="matrix(.87464 0 0 .87464 14.731 23.409)" fill="#1793d1">
<path d="m143.89 121.65c-10.773 26.408-17.271 43.681-29.266 69.304 7.3541 7.7938 16.381 16.87 31.04 27.121-15.76-6.484-26.512-12.993-34.544-19.748-15.35 32.025-39.4 77.644-88.206 165.31 38.36-22.142 68.095-35.791 95.807-41-1.19-5.117-1.8665-10.652-1.8206-16.427l0.0455-1.2286c0.60867-24.57 13.393-43.465 28.537-42.183 15.144 1.2829 26.915 22.254 26.306 46.824-0.11452 4.6234-0.63609 9.0711-1.5475 13.196 27.411 5.361 56.828 18.977 94.669 40.818-7.4614-13.735-14.121-26.114-20.481-37.905-10.018-7.7631-20.467-17.866-41.781-28.805 14.651 3.806 25.14 8.1972 33.316 13.105-64.664-120.37-69.9-136.36-92.074-188.39z" fill-rule="evenodd" stroke-width="1.2737"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 919 B

13
docs/guides/cli.rst Normal file
View File

@ -0,0 +1,13 @@
As a CLI user, I want to...
===========================
These sections describe specific problems that you may want to solve using
the command-line (CLI) utilities provided by Cahute.
.. toctree::
:maxdepth: 1
cli/get-info
cli/send-file
cli/get-file
cli/display-screen

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,34 @@
.. _guide-cli-display-screen:
As a CLI user, I want to display the screen from my calculator
==============================================================
.. warning::
This guide only works for calculators connected by USB. If you are using
a serial link, no option is available to you with Cahute yet!
In order to display the screen from a CASIO calculator, the steps are the
following:
1. Connect the calculator to the PC using a USB cable.
2. Select a screenstreaming mode on the USB calculator.
3. Run ``p7screen``.
Once the calculator is connected, a prompt with various protocols is displayed:
* On fx9860G and compatible (monochrome calculators), select
``Projector`` (F3).
* On fx-CG and compatible (color calculators), select
``ScreenR(XP)`` (F3) or, if it not available, any mode between
``Projector`` (F4) or ``ScreenRecv`` (F2).
Once this is done, in a terminal, run the following command::
p7screen
.. figure:: display-screen.png
p7screen displaying the screen from a Graph 90+E in ``ScreenR(XP)`` mode.
For more options, see :ref:`p7screen`.

View File

@ -0,0 +1,72 @@
.. _guide-cli-get-file:
As a CLI user, I want to get a file from the calculator's storage memory
========================================================================
.. warning::
This guide is currently only available for fx-9860G and compatible
calculators, excluding the fx-CG line of calculators, which need to
be used as a normal storage device ("USB Key" mode).
In this guide, we will assume you want to transfer the ``MYADDIN.G1A`` file
from your calculator.
The instructions vary depending on the way your calculator is connected,
see the correct section for your case.
For more options, see :ref:`p7` and :ref:`p7-get`.
If you are using USB
--------------------
The steps are the following:
1. Connect the calculator to the PC using a mini USB cable.
2. Select the ``DataTrans`` (``TransfDon`` in French) mode when prompted.
3. Run the transfer from the PC using ``p7 get``.
Once steps 1 and 2 are done, run the following command::
p7 get MYADDIN.G1A
The file should have been successfully transferred from the calculator!
If you are using serial
-----------------------
The steps are the following:
1. Connect the calculator to the PC using a USB to serial cable.
2. Configure the calculator to use serial and data transfer, and place
the calculator in standby mode.
3. Find out which serial port your calculator is connected on.
4. Run the transfer from the PC using ``p7 get``.
For step 2, go to your link application, and do the following:
* If a ``CABL`` (F4) submenu is available, go into it and press F2
to select "3-pin" ("3 broches" in French);
* Go to the ``CAPT`` (F6) submenu and press F1 to select "Memory"
("Mémoire" in French).
* Place your calculator in receiving mode by pressing F2 to select the
``RECV`` submenu.
For step 3, you must run the following command to discover all available
serial ports p7 can use:
.. code-block:: bash
p7 list-devices
Unfortunately, if there are several ports, there is no easy way to
find out which port is the right one to use; you will need to try
each of them.
For step 4, you can run the transfer by using the following command:
.. code-block:: bash
p7 --com <your-serial-device> get MYADDIN.G1A
The file should have been successfully transferred from the calculator!

View File

@ -0,0 +1,69 @@
.. _guide-cli-get-info:
As a CLI user, I want to get information regarding my calculator
================================================================
.. warning::
This guide is currently only available for fx-9860G and compatible
calculators, excluding the fx-CG line of calculators, which need to
be used as a normal storage device ("USB Key" mode).
The instructions vary depending on the way your calculator is connected,
see the correct section for your case.
For more options, see :ref:`p7` and :ref:`p7-info`.
If you are using USB
--------------------
The steps are the following:
1. Connect the calculator to the PC using a mini USB cable.
2. Select the ``DataTrans`` (``TransfDon`` in French) mode when prompted.
3. Run the command from the PC using ``p7 info``.
Once steps 1 and 2 are done, run the following command::
p7 info
Your calculator's information should have been gathered and displayed!
If you are using serial
-----------------------
The steps are the following:
1. Connect the calculator to the PC using a USB to serial cable.
2. Configure the calculator to use serial and data transfer, and place
the calculator in standby mode.
3. Find out which serial port your calculator is connected on.
4. Run the command from the PC using ``p7 info``.
For step 2, go to your link application, and do the following:
* If a ``CABL`` (F4) submenu is available, go into it and press F2
to select "3-pin" ("3 broches" in French);
* Go to the ``CAPT`` (F6) submenu and press F1 to select "Memory"
("Mémoire" in French).
* Place your calculator in receiving mode by pressing F2 to select the
``RECV`` submenu.
For step 3, you must run the following command to discover all available
serial ports p7 can use:
.. code-block:: bash
p7 list-devices
Unfortunately, if there are several ports, there is no easy way to
find out which port is the right one to use; you will need to try
each of them.
For step 4, you can run the transfer by using the following command:
.. code-block:: bash
p7 --com <your-serial-device> info
Your calculator's information should have been gathered and displayed!

View File

@ -0,0 +1,84 @@
.. _guide-cli-send-file:
As a CLI user, I want to send a file to the calculator's storage memory
=======================================================================
.. warning::
This guide is currently only available for fx-9860G and compatible
calculators, excluding the fx-CG line of calculators, which need to
be used as a normal storage device ("USB Key" mode).
In this guide, we will assume you want to transfer the ``MYADDIN.G1A`` file
from your PC to your calculator.
The instructions vary depending on the way your calculator is connected,
see the correct section for your case.
For more options, see :ref:`p7` and :ref:`p7-send`.
If you are using USB
--------------------
The steps are the following:
1. Connect the calculator to the PC using a mini USB cable.
2. Select the ``DataTrans`` (``TransfDon`` in French) mode when prompted.
3. Run the transfer from the PC using ``p7 send``.
Once steps 1 and 2 are done, run the following command::
p7 send MYADDIN.G1A
If the file already exists, you will be prompted on whether you want to
overwrite the file on the calculator. In this case, you can either:
* Enter ``y`` and press enter, to confirm the overwrite.
* Enter ``n`` and press enter, to reject the overwrite.
Your file has successfully been transferred to the calculator!
If you are using serial
-----------------------
The steps are the following:
1. Connect the calculator to the PC using a USB to serial cable.
2. Configure the calculator to use serial and data transfer, and place
the calculator in standby mode.
3. Find out which serial port your calculator is connected on.
4. Run the transfer from the PC using ``p7 send``.
For step 2, go to your link application, and do the following:
* If a ``CABL`` (F4) submenu is available, go into it and press F2
to select "3-pin" ("3 broches" in French);
* Go to the ``CAPT`` (F6) submenu and press F1 to select "Memory"
("Mémoire" in French).
* Place your calculator in receiving mode by pressing F2 to select the
``RECV`` submenu.
For step 3, you must run the following command to discover all available
serial ports p7 can use:
.. code-block:: bash
p7 list-devices
Unfortunately, if there are several ports, there is no easy way to
find out which port is the right one to use; you will need to try
each of them.
For step 4, you can run the transfer by using the following command:
.. code-block:: bash
p7 --com <your-serial-device> send MYADDIN.G1A
If the file already exists, you will be prompted on whether you want to
overwrite the file on the calculator. In this case, you can either:
* Enter ``y`` and press enter, to confirm the overwrite.
* Enter ``n`` and press enter, to reject the overwrite.
Your file has successfully been transferred to the calculator!

View File

@ -0,0 +1,88 @@
Contributing to Cahute
======================
You have something to contribute to Cahute, whether it's a new feature, a tweak
on an existing protocol, an unhandled file format, some more background on
a feature or problematic, or a fix to a typo in the documentation?
First of all, thank you so much! This guide will help you set up for efficient
contribution, and save some time to all involved.
.. note::
If you are interested in the rationale of what is indicated here,
you can consult the :ref:`contribution-style` for more information.
Preparing the repository
------------------------
You must fork Cahute's repository, by:
* Creating an account on `Gitlab.com`_.
* Going on the Cahute repository and selecting "Fork".
* Cloning the fork on your PC, by running a command resembling
``git clone https://gitlab.com/cahuteproject/cahute.git``.
* Changing the current branch to a work branch, by e.g.
running ``git switch -c feat/your-goal-here``.
Installing and setting up the required tools
--------------------------------------------
You will need the following dependencies and tools:
* Build dependencies for the project; see :ref:`build-cahute`.
* Python >= 3.11, with dependencies listed in ``docs/requirements.txt``,
which you can either install externally, with a virtualenv or directly,
using ``pip install -r docs/requirements.txt``.
* `pre-commit`_.
In order to set up pre-commit, you need to run the following command::
pre-commit install
If you are modifying the code, even just when fixing a typo, you need to
build the project from the modified source; see :ref:`build-cahute` for
more information.
Testing your changes
--------------------
If you have only updated the documentation, you can run
``make -C docs preview``, which will run a webserver on
``http://localhost:8000`` to allow you to preview your changes.
If you have updated the code, you must build the project, then run
either ``./p7 <your command args here...>``,
``./p7screen <your command args here...>``, or any other custom
executable you link with the built ``./libcahute.a`` to test it.
.. note::
It is recommended to describe the tests that you have done in the
description of the merge request, so that the maintainers can have
an idea of what you could have missed!
Pushing your changes
--------------------
Once you are done, you must commit and push your changes.
To create your commit, you most certainly want to do something like
``git add .``, then ``git commit -m "your message here"``.
Note that the message must be formatted a certain way:
* If you are adding a feature: ``feat: your message here``, e.g.
``feat: add hypertriangulation to protocol delta``.
* If you are fixing a bug in the code: ``fix: your message here``, e.g.
``fix: fix bad buffer usage by TYPKZ OHP packets``.
* If you are adding something to or fixing something in the docs:
``docs: your message here``, e.g.
``docs: add missing section for hypertriangulation flow``.
You can now push to your repository using
``git push -u origin feat/your-goal-here`` (*replace the branch name*).
Gitlab will provide you the link to create the merge request.
.. _Gitlab.com: https://gitlab.com/
.. _pre-commit: https://pre-commit.com/
.. _Sphinx: https://www.sphinx-doc.org/en/master/

13
docs/guides/developer.rst Normal file
View File

@ -0,0 +1,13 @@
As a developer, I want to...
============================
These sections describe specific problems that you may want to solve using
the Cahute C library directly.
.. toctree::
:maxdepth: 1
developer/build
developer/detect-usb
developer/detect-serial
developer/open-usb-link

View File

@ -0,0 +1,50 @@
As a developer, I want to use the Cahute library in my project
==============================================================
.. warning::
This guide assumes that you have already installed the Cahute library.
If you have not, see :ref:`guide-install`.
The instructions to build your project using the Cahute library depend on
your build system; please read the correct section for yours!
Using |cmake| CMake
-------------------
If your project is built using CMake, you can add the following to your
``CMakeLists.txt`` file, before defining your targets, to include and start
building with Cahute:
.. code-block:: cmake
pkg_check_modules(cahute REQUIRED cahute IMPORTED_TARGET)
link_libraries(PkgConfig::cahute)
Using pkg-config
----------------
Cahute defines the ``cahute`` pkg-config configuration. Therefore:
* You can obtain the compilation flags by running the following command:
.. code-block:: bash
pkg-config cahute --cflags
* You can obtain the linking flags by running the following command:
.. code-block:: bash
pkg-config cahute --libs
For example, if you want to compile a simple project using Cahute, you can
use the following command:
.. code-block:: bash
cc main.c -o ./my_util `pkg-config cahute --cflags --libs`
.. |cmake| image:: cmake.svg
.. _CMake: https://cmake.org/

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
x="0px"
y="0px"
viewBox="0 0 64 63.999998"
xml:space="preserve"
version="1.1"
id="svg39"
sodipodi:docname="cmake-icon.svg"
width="64"
height="64"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><defs
id="defs43"> </defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1001"
id="namedview41"
showgrid="false"
inkscape:zoom="2.4266285"
inkscape:cx="69.849998"
inkscape:cy="22.700001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg39" /> <style
type="text/css"
id="style2"> .st0{fill:#064F8C;} .st1{fill:#249847;} .st2{fill:#BE2128;} .st3{fill:#172C36;} .st4{fill:#CDCDCE;} </style> <g
id="Layer_2"
transform="translate(0,18.599998)"> </g> <g
id="g922"
transform="matrix(2.2190473,0,0,2.2190473,-14.603456,-34.571419)"><polygon
transform="matrix(0.79452357,0,0,0.79452357,3.8001251,12.63966)"
class="st0"
points="21.3,3.8 3.6,38.8 22.9,22.4 "
id="polygon6"
style="fill:#064f8c" /><polygon
transform="matrix(0.79452357,0,0,0.79452357,3.8001251,12.63966)"
class="st1"
points="38.6,39.9 14.8,30.3 3.5,39.9 "
id="polygon8"
style="fill:#249847" /><polygon
transform="matrix(0.79452357,0,0,0.79452357,3.8001251,12.63966)"
class="st2"
points="39.8,39.6 22.1,4.4 24.7,33.5 "
id="polygon10"
style="fill:#be2128" /><polygon
transform="matrix(0.79452357,0,0,0.79452357,3.8001251,12.63966)"
style="fill:#cdcdce"
id="polygon34"
points="23.9,33.2 23,23.3 15.4,29.8 "
class="st4" /></g> </svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,21 @@
/* Compile using: gcc detect-serial.c `pkg-config cahute --cflags --libs`. */
#include <stdio.h>
#include <cahute.h>
int my_callback(void *cookie, cahute_serial_detection_entry const *entry) {
printf("New entry data:\n");
printf("- %s\n", entry->cahute_serial_detection_entry_name);
return 0;
}
int main(void) {
int err;
err = cahute_detect_serial(&my_callback, NULL);
if (err)
fprintf(stderr, "Cahute has returned error 0x%04X.\n", err);
return 0;
}

View File

@ -0,0 +1,20 @@
.. _guide-developer-detect-serial:
As a developer, I want to list available serial ports
=====================================================
In order to list calculators connected by USB, you must use
:c:func:`cahute_detect_serial`, while providing it with a callback to either
add the USB device to your list or display it.
An example program using this function is the following:
.. literalinclude:: detect-serial.c
:language: c
An example output for this program is the following:
.. code-block:: text
New entry data:
- Path: /dev/ttyUSB0

View File

@ -0,0 +1,41 @@
/* Compile using: gcc detect-usb.c `pkg-config cahute --cflags --libs`. */
#include <stdio.h>
#include <cahute.h>
int my_callback(void *cookie, cahute_usb_detection_entry const *entry) {
char const *type_name;
switch (entry->cahute_usb_detection_entry_type) {
case CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN:
type_name = "fx-9860G or compatible";
break;
case CAHUTE_USB_DETECTION_ENTRY_TYPE_SCSI:
type_name = "fx-CG or compatible";
break;
default:
type_name = "(unknown)";
}
printf("New entry data:\n");
printf(
"- Address: %03d:%03d\n",
entry->cahute_usb_detection_entry_bus,
entry->cahute_usb_detection_entry_address
);
printf("- Type: %s\n", type_name);
return 0;
}
int main(void) {
int err;
err = cahute_detect_usb(&my_callback, NULL);
if (err)
fprintf(stderr, "Cahute has returned error 0x%04X.\n", err);
return 0;
}

View File

@ -0,0 +1,37 @@
.. _guide-developer-detect-usb:
As a developer, I want to list calculators connected by USB
===========================================================
In order to list calculators connected by USB, you must use
:c:func:`cahute_detect_usb`, while providing it with a callback to either
add the USB device to your list or display it.
An example program using this function is the following:
.. literalinclude:: detect-usb.c
:language: c
An example output for this program is the following:
.. code-block:: text
New entry data:
- Address: 003:042
- Type: fx-9860G or compatible
New entry data:
- Address: 003:043
- Type: fx-CG or compatible
.. note::
On Linux, you can compare this output to the output of ``lsusb``, for
example in the case above:
.. code-block:: text
$ lsusb
...
Bus 003 Device 042: ID 07cf:6101 Casio Computer Co., Ltd fx-9750gII
Bus 003 Device 043: ID 07cf:6102 Casio Computer Co., Ltd fx-CP400
...

View File

@ -0,0 +1,58 @@
/* Compile using: gcc open-usb-link.c `pkg-config cahute --cflags --libs`. */
#include <stdio.h>
#include <cahute.h>
struct my_detection_cookie {
int found_bus;
int found_address;
};
int my_detection_callback(
struct my_detection_cookie *cookie,
cahute_usb_detection_entry const *entry
) {
/* We only want fx-9860G and compatible. */
if (entry->cahute_usb_detection_entry_type
!= CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN)
return 0; /* Continue. */
cookie->found_bus = entry->cahute_usb_detection_entry_bus;
cookie->found_address = entry->cahute_usb_detection_entry_address;
return 1; /* Interrupt and return CAHUTE_ERROR_INT. */
}
int main(void) {
struct my_detection_cookie cookie;
cahute_link *link;
int err;
/* This either returns 0 if detection has gotten to the end without finding
* a suitable device, CAHUTE_ERROR_INT if a suitable device has been
* found (due to our callback having returned 1), or another error. */
err = cahute_detect_usb(
(cahute_detect_usb_entry_func *)&my_detection_callback,
&cookie
);
if (!err) {
fprintf(stderr, "No USB calculator detected.\n");
return 1;
}
if (err != CAHUTE_ERROR_INT) {
fprintf(stderr, "cahute_detect_usb has returned error 0x%04X.\n", err);
return 1;
}
err =
cahute_open_usb_link(&link, 0, cookie.found_bus, cookie.found_address);
if (err) {
fprintf(stderr, "cahute_open_usb has returned error 0x%04X.\n", err);
return 1;
}
printf("Link successfully opened!\n");
cahute_close_link(link);
return 0;
}

View File

@ -0,0 +1,16 @@
As a developer, I want to open a link to a calculator connected by USB
======================================================================
In order to open the link to the calculator, the steps are the following:
1. Either request USB device address from the user, or use USB device
detection to find out the first available device; see
:ref:`guide-developer-detect-usb` for more information.
2. Call :c:func:`cahute_open_usb_link` with the device address.
3. *Profit!*
4. Call :c:func:`cahute_close_link` to close the link.
An example program to do this is the following:
.. literalinclude:: open-usb-link.c
:language: c

135
docs/guides/install.rst Normal file
View File

@ -0,0 +1,135 @@
.. _guide-install:
Installing Cahute
=================
In order to install Cahute's library and command-line utilities, the
instructions depends on the system you want to install it on.
.. note::
The guides expect ``p7`` and ``p7screen`` to be directly usable in your
command-line shell at the end of the installation process, i.e. that:
* Either both executables are placed in a directory already referenced
by your ``$PATH``;
* Or the directory in which both executables are placed is added to
your ``$PATH``, and your shell has been updated to take the change
into account.
If neither of these is the case for your system, you will need to
tweak the commands to point to the right executable in an absolute or
relative fashion, e.g. ``/opt/cahute/bin/p7 info``.
|archlinux| Archlinux, |manjaro| Manjaro Linux
----------------------------------------------
Cahute and its command-line utilities are present on the
`Archlinux User Repository`_, you can pop up your favourite pacman frontend
and install the `cahute <cahute on AUR_>`_ package:
* Using paru_::
paru -S cahute
* Using pikaur_::
pikaur -S cahute
Once installed, it is recommended to add your user to the ``uucp`` group,
for access to serial and USB devices, by running the following command
**as root** then restarting your session::
usermod -a -G uucp <your-username>
.. _build-cahute:
|linux| Other Linux distributions
---------------------------------
.. note::
This guide may not be exhaustive, and a package may exist for your
distribution. Please check with your distribution's package registry
and/or wiki before proceeding!
If no package exists for your distribution, or you are to package Cahute for
your distribution, you can build the command-line utilities yourself.
First, you need to install the build and runtime dependencies for Cahute:
* cmake_ >= 3.16;
* `GNU Make`_, `pkg-config`_, and other C compilation and linking utilities;
* SDL_ >= 2.0 (for ``p7screen``);
* libusb_.
For getting the source, you have the following options:
* You can download the latest source package at
https://ftp.cahuteproject.org/releases\ :
.. parsed-literal::
curl -o cahute-|version|.tar.gz https\://ftp.cahuteproject.org/releases/cahute-|version|.tar.gz
tar xvaf cahute-|version|.tar.gz
* You can clone the repository and checkout the tag corresponding to the
release:
.. parsed-literal::
git clone https\://gitlab.com/cahuteproject/cahute.git cahute-|version|
(cd cahute-|version| && git checkout -f |version|)
The project is present in the "cahute-|version|" directory.
In the parent directory, we are to create the ``build`` directory aside
it, and install from it, by running the following commands:
.. parsed-literal::
cmake -B build -S cahute-|version| -DCMAKE_INSTALL_PREFIX=/usr
cmake --build build
sudo cmake --install build
.. warning::
For communicating with calculators over USB and serial, Cahute library
and command-line utilities require access to such devices.
For serial devices, this is traditionally represented by being a member
of the ``uucp`` group, defined as the group owner on ``/dev/ttyS*``
devices; you can check this by running ``ls -l /dev/ttyS*``.
However, by default, USB devices don't have such rules.
CMake automatically installs the udev rules, which means you need to
do the following:
* Reload the udev daemon reload to apply the newly installed rules
on the running system without a reboot, with this command **as root**::
udevadm control --reload
* Adding your user to the ``uucp`` group, then restarting your session::
usermod -a -G uucp <your-username>
That's it! You should be able to run the following command::
p7 --version
.. _Archlinux User Repository: https://aur.archlinux.org/
.. _cahute on AUR: https://aur.archlinux.org/packages/cahute
.. _p7 on AUR: https://aur.archlinux.org/packages/p7
.. _p7screen on AUR: https://aur.archlinux.org/packages/p7screen
.. _paru: https://github.com/morganamilo/paru
.. _pikaur: https://github.com/actionless/pikaur
.. _cmake: https://cmake.org/
.. _GNU Make: https://www.gnu.org/software/make/
.. _pkg-config: https://git.sr.ht/~kaniini/pkgconf
.. _SDL: https://www.libsdl.org/
.. _libusb: https://libusb.info/
.. |archlinux| image:: arch.svg
.. |manjaro| image:: manjaro.svg
.. |linux| image:: linux.svg

348
docs/guides/linux.svg Normal file
View File

@ -0,0 +1,348 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256" height="256" version="1.1" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Tux</title>
<defs id="tux_fx">
<linearGradient id="gradient_belly_shadow">
<stop offset="0"/>
<stop stop-opacity=".25" offset="1"/>
</linearGradient>
<linearGradient id="gradient_wing_tip_right_shadow">
<stop stop-color="#110800" offset="0"/>
<stop stop-color="#a65a00" stop-opacity=".8" offset=".59"/>
<stop stop-color="#ff921e" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="gradient_eyeball">
<stop stop-color="#fefefc" offset="0"/>
<stop stop-color="#fefefc" offset=".75"/>
<stop stop-color="#d4d4d4" offset="1"/>
</linearGradient>
<linearGradient id="gradient_eyebrow">
<stop stop-color="#646464" stop-opacity="0" offset="0"/>
<stop stop-color="#646464" stop-opacity=".58" offset=".31"/>
<stop stop-color="#646464" offset=".47"/>
<stop stop-color="#646464" stop-opacity=".26" offset=".73"/>
<stop stop-color="#646464" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="gradient_nares">
<stop stop-color="#3a2903" offset="0"/>
<stop stop-color="#735208" offset=".55"/>
<stop stop-color="#ac8c04" offset="1"/>
</linearGradient>
<radialGradient id="fill_belly_shadow_left" cx="0" cy="0" r="1" gradientTransform="matrix(19,0,0,18,61.18,121.19)" gradientUnits="userSpaceOnUse" href="#gradient_belly_shadow" xlink:href="#gradient_belly_shadow"/>
<radialGradient id="fill_belly_shadow_right" cx="0" cy="0" r="1" gradientTransform="matrix(23.6,0,0,18,125.74,131.6)" gradientUnits="userSpaceOnUse" href="#gradient_belly_shadow" xlink:href="#gradient_belly_shadow"/>
<radialGradient id="fill_belly_shadow_middle" cx="0" cy="0" r="1" gradientTransform="matrix(9.35,0,0,10,94.21,127.47)" gradientUnits="userSpaceOnUse" href="#gradient_belly_shadow" xlink:href="#gradient_belly_shadow"/>
<linearGradient id="fill_foot_left_base" x1="23.18" x2="64.31" y1="193.01" y2="262.02" gradientUnits="userSpaceOnUse" href="#gradient_foot_left_layer_1">
<stop stop-color="#b98309" offset="0"/>
<stop stop-color="#382605" offset="1"/>
</linearGradient>
<linearGradient id="fill_foot_left_glare" x1="64.47" x2="77.41" y1="210.83" y2="235.21" gradientUnits="userSpaceOnUse" href="#gradient_foot_left_glare">
<stop stop-color="#ebc40c" offset="0"/>
<stop stop-color="#ebc40c" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="fill_foot_right_shadow" x1="146.93" x2="150.2" y1="211.96" y2="235.73" gradientUnits="userSpaceOnUse" href="#gradient_foot_right_shadow">
<stop offset="0"/>
<stop stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="fill_foot_right_base" x1="151.5" x2="192.94" y1="253.02" y2="185.84" gradientUnits="userSpaceOnUse" href="#gradient_foot_right_layer_1">
<stop stop-color="#3e2a06" offset="0"/>
<stop stop-color="#ad780a" offset="1"/>
</linearGradient>
<linearGradient id="fill_foot_right_glare" x1="162.81" x2="161.59" y1="180.67" y2="191.64" gradientUnits="userSpaceOnUse" href="#gradient_foot_right_glare">
<stop stop-color="#f3cd0c" offset="0"/>
<stop stop-color="#f3cd0c" stop-opacity="0" offset="1"/>
</linearGradient>
<radialGradient id="fill_wing_tip_right_shadow_lower" cx="0" cy="0" r="1" gradientTransform="matrix(18.99 5.0884 -5.342 19.937 169.71 194.53)" gradientUnits="userSpaceOnUse" href="#gradient_wing_tip_right_shadow" xlink:href="#gradient_wing_tip_right_shadow"/>
<radialGradient id="fill_wing_tip_right_shadow_upper" cx="0" cy="0" r="1" gradientTransform="matrix(19.722 -.83351 .62745 14.847 169.71 189.89)" gradientUnits="userSpaceOnUse" href="#gradient_wing_tip_right_shadow" xlink:href="#gradient_wing_tip_right_shadow"/>
<radialGradient id="fill_wing_tip_right_glare_1" cx="0" cy="0" r="1" gradientTransform="matrix(6.3736 2.7713 -1.28 2.9438 184.65 176.62)" gradientUnits="userSpaceOnUse" href="#gradient_wing_tip_right_glare_1">
<stop stop-color="#7c7c7c" offset="0"/>
<stop stop-color="#7c7c7c" stop-opacity=".33" offset="1"/>
</radialGradient>
<linearGradient id="fill_wing_tip_right_glare_2" x1="165.69" x2="168.27" y1="173.58" y2="173.47" gradientUnits="userSpaceOnUse" href="#gradient_wing_tip_right_glare_2">
<stop stop-color="#7c7c7c" offset="0"/>
<stop stop-color="#7c7c7c" stop-opacity=".33" offset="1"/>
</linearGradient>
<radialGradient id="fill_eyeball_left" cx="0" cy="0" r="1" gradientTransform="matrix(10.239 -.10723 .1642 15.679 86.49 51.41)" gradientUnits="userSpaceOnUse" href="#gradient_eyeball" xlink:href="#gradient_eyeball"/>
<linearGradient id="fill_pupil_left_glare" x1="84.29" x2="89.32" y1="46.64" y2="55.63" gradientUnits="userSpaceOnUse" href="#gradient_pupil_left_glare">
<stop stop-color="#757574" stop-opacity="0" offset="0"/>
<stop stop-color="#757574" offset=".25"/>
<stop stop-color="#757574" offset=".5"/>
<stop stop-color="#757574" stop-opacity="0" offset="1"/>
</linearGradient>
<radialGradient id="fill_eyelid_left" cx="0" cy="0" r="1" gradientTransform="matrix(6.167 -1.0154 .93742 5.6933 84.89 43.74)" gradientUnits="userSpaceOnUse" href="#gradient_eyelid_left">
<stop stop-color="#c8c8c8" offset="0"/>
<stop stop-color="#797978" offset="1"/>
</radialGradient>
<linearGradient id="fill_eyebrow_left" x1="83.59" x2="94.48" y1="32.51" y2="43.63" gradientUnits="userSpaceOnUse" href="#gradient_eyebrow" xlink:href="#gradient_eyebrow"/>
<radialGradient id="fill_eyeball_right" cx="0" cy="0" r="1" gradientTransform="matrix(13.633 -.42844 .49252 15.672 118.06 51.41)" gradientUnits="userSpaceOnUse" href="#gradient_eyeball" xlink:href="#gradient_eyeball"/>
<linearGradient id="fill_pupil_right_glare" x1="117.87" x2="123.66" y1="47.25" y2="54.11" gradientUnits="userSpaceOnUse" href="#gradient_pupil_right_glare_2">
<stop stop-color="#949494" stop-opacity=".39" offset="0"/>
<stop stop-color="#949494" offset=".5"/>
<stop stop-color="#949494" stop-opacity=".39" offset="1"/>
</linearGradient>
<linearGradient id="fill_eyelid_right" x1="112.9" x2="131.32" y1="36.23" y2="47.01" gradientUnits="userSpaceOnUse" href="#gradient_eyelid_right">
<stop stop-color="#747474" offset="0"/>
<stop stop-color="#8c8c8c" offset=".13"/>
<stop stop-color="#a4a4a4" offset=".25"/>
<stop stop-color="#d4d4d4" offset=".5"/>
<stop stop-color="#d4d4d4" offset=".62"/>
<stop stop-color="#7c7c7c" offset="1"/>
</linearGradient>
<linearGradient id="fill_eyebrow_right" x1="119.16" x2="131.42" y1="31.56" y2="43.14" gradientUnits="userSpaceOnUse" href="#gradient_eyebrow" xlink:href="#gradient_eyebrow"/>
<radialGradient id="fill_beak_base" cx="0" cy="0" r="1" gradientTransform="matrix(9.2552 -6.7243 6.1012 8.3976 97.64 60.12)" gradientUnits="userSpaceOnUse" href="#gradient_beak_base">
<stop stop-color="#020204" offset="0"/>
<stop stop-color="#020204" offset=".73"/>
<stop stop-color="#5c5c5c" offset="1"/>
</radialGradient>
<radialGradient id="fill_mandible_lower_base" cx="0" cy="0" r="1" gradientTransform="matrix(25.101 -10.346 7.267 17.631 109.77 70.61)" gradientUnits="userSpaceOnUse" href="#gradient_mandible_lower">
<stop stop-color="#d2940a" offset="0"/>
<stop stop-color="#d89c08" offset=".75"/>
<stop stop-color="#b67e07" offset=".87"/>
<stop stop-color="#946106" offset="1"/>
</radialGradient>
<linearGradient id="fill_mandible_upper_base" x1="78.09" x2="126.77" y1="69.26" y2="68.88" gradientUnits="userSpaceOnUse" href="#gradient_mandible_upper">
<stop stop-color="#ad780a" offset="0"/>
<stop stop-color="#d89e08" offset=".12"/>
<stop stop-color="#edb80b" offset=".25"/>
<stop stop-color="#ebc80d" offset=".39"/>
<stop stop-color="#f5d838" offset=".53"/>
<stop stop-color="#f6d811" offset=".77"/>
<stop stop-color="#f5cd31" offset="1"/>
</linearGradient>
<radialGradient id="fill_naris_left" cx="0" cy="0" r="1" gradientTransform="matrix(1.32,0,0,1.42,92.11,59.88)" gradientUnits="userSpaceOnUse" href="#gradient_nares" xlink:href="#gradient_nares"/>
<radialGradient id="fill_naris_right" cx="0" cy="0" r="1" gradientTransform="matrix(2.78,0,0,1.62,104.65,59.7)" gradientUnits="userSpaceOnUse" href="#gradient_nares" xlink:href="#gradient_nares"/>
<linearGradient id="fill_beak_corner" x1="126.74" x2="126.74" y1="67.49" y2="71.09" gradientUnits="userSpaceOnUse" href="#gradient_beak_corner">
<stop stop-color="#f5ce2d" offset="0"/>
<stop stop-color="#d79b08" offset="1"/>
</linearGradient>
<filter id="blur_belly_shadow_left" x="-.049919" y="-.045849" width="1.0998" height="1.0917">
<feGaussianBlur stdDeviation="0.64 0.55"/>
</filter>
<filter id="blur_belly_shadow_right" x="-.050747" y="-.073203" width="1.1015" height="1.1464">
<feGaussianBlur stdDeviation="0.98"/>
</filter>
<filter id="blur_belly_shadow_middle" x="-.087319" y="-.081682" width="1.1746" height="1.1634">
<feGaussianBlur stdDeviation="0.68"/>
</filter>
<filter id="blur_belly_shadow_lower" x="-.81954" y="-.057993" width="2.6391" height="1.116">
<feGaussianBlur stdDeviation="1.25"/>
</filter>
<filter id="blur_belly_glare" x="-.49197" y="-.59291" width="1.9839" height="2.1858">
<feGaussianBlur stdDeviation="1.78 2.19"/>
</filter>
<filter id="blur_head_glare" x="-.29258" y="-.30859" width="1.5852" height="1.6172">
<feGaussianBlur stdDeviation="1.73"/>
</filter>
<filter id="blur_neck_glare" x="-.17841" y="-.16017" width="1.3568" height="1.3203">
<feGaussianBlur stdDeviation="0.78"/>
</filter>
<filter id="blur_wing_left_glare" x="-.10145" y="-.042862" width="1.2029" height="1.0857">
<feGaussianBlur stdDeviation="0.98"/>
</filter>
<filter id="blur_wing_right_glare" x="-.1179" y="-.040212" width="1.2358" height="1.0804">
<feGaussianBlur stdDeviation="1.19 1.17"/>
</filter>
<filter id="blur_foot_left_layer_1" x="-.10746" y="-.10845" width="1.2149" height="1.2169">
<feGaussianBlur stdDeviation="3.38"/>
</filter>
<filter id="blur_foot_left_layer_2" x="-.075378" y="-.069657" width="1.1508" height="1.1393">
<feGaussianBlur stdDeviation="2.1 2.06"/>
</filter>
<filter id="blur_foot_left_glare" x="-.033862" y="-.018668" width="1.0677" height="1.0373">
<feGaussianBlur stdDeviation="0.32"/>
</filter>
<filter id="blur_foot_right_shadow" x="-.076044" y="-.068073" width="1.1521" height="1.1361">
<feGaussianBlur stdDeviation="1.95 1.9"/>
</filter>
<filter id="blur_foot_right_layer_1" x="-.14509" y="-.14307" width="1.2902" height="1.2861">
<feGaussianBlur stdDeviation="4.12"/>
</filter>
<filter id="blur_foot_right_layer_2" x="-.11511" y="-.12498" width="1.2302" height="1.25">
<feGaussianBlur stdDeviation="3.12 3.37"/>
</filter>
<filter id="blur_foot_right_glare" x="-.020751" y="-.12827" width="1.0415" height="1.2565">
<feGaussianBlur stdDeviation="0.41"/>
</filter>
<filter id="blur_wing_tip_right_shadow_lower" x="-.20044" y="-.23395" width="1.4009" height="1.4679">
<feGaussianBlur stdDeviation="2.45"/>
</filter>
<filter id="blur_wing_tip_right_shadow_upper" x="-.091629" y="-.10698" width="1.1833" height="1.214">
<feGaussianBlur stdDeviation="1.12 0.81"/>
</filter>
<filter id="blur_wing_tip_right_glare" x="-.070171" y="-.14998" width="1.1403" height="1.3">
<feGaussianBlur stdDeviation="0.88"/>
</filter>
<filter id="blur_pupil_left_glare" x="-.23333" y="-.14544" width="1.4667" height="1.2909">
<feGaussianBlur stdDeviation="0.44"/>
</filter>
<filter id="blur_eyebrow_left" x="-.023848" y="-.026523" width="1.0477" height="1.053">
<feGaussianBlur stdDeviation="0.12"/>
</filter>
<filter id="blur_pupil_right_glare" x="-.17959" y="-.18232" width="1.3592" height="1.3646">
<feGaussianBlur stdDeviation="0.45"/>
</filter>
<filter id="blur_eyebrow_right" x="-.023601" y="-.025574" width="1.0472" height="1.0511">
<feGaussianBlur stdDeviation="0.13"/>
</filter>
<filter id="blur_beak_shadow_lower" x="-.076202" y="-.12888" width="1.1524" height="1.2578">
<feGaussianBlur stdDeviation="1.75"/>
</filter>
<filter id="blur_beak_shadow_upper" x="-.036392" y="-.074089" width="1.0728" height="1.1482">
<feGaussianBlur stdDeviation="0.8 0.74"/>
</filter>
<filter id="blur_mandible_lower_glare" x="-.15437" y="-.21067" width="1.3087" height="1.4213">
<feGaussianBlur stdDeviation="0.77"/>
</filter>
<filter id="blur_mandible_upper_shadow" x="-.034221" y="-.078979" width="1.0684" height="1.158">
<feGaussianBlur stdDeviation="0.65"/>
</filter>
<filter id="blur_mandible_upper_glare" x="-.16065" y="-.13004" width="1.3213" height="1.2601">
<feGaussianBlur stdDeviation="0.73"/>
</filter>
<filter id="blur_naris_left" x="-.12244" y="-.11124" width="1.2449" height="1.2225">
<feGaussianBlur stdDeviation="0.1"/>
</filter>
<filter id="blur_naris_right" x="-.049122" y="-.093498" width="1.0982" height="1.187">
<feGaussianBlur stdDeviation="0.1"/>
</filter>
<filter id="blur_beak_corner" x="-.11091" y="-.09064" width="1.2218" height="1.1813">
<feGaussianBlur stdDeviation="0.23"/>
</filter>
<clipPath id="clip_body">
<use href="#body_base" xlink:href="#body_base"/>
</clipPath>
<clipPath id="clip_wing_left">
<use href="#wing_left_base" xlink:href="#wing_left_base"/>
</clipPath>
<clipPath id="clip_wing_right">
<use href="#wing_right_base" xlink:href="#wing_right_base"/>
</clipPath>
<clipPath id="clip_foot_left">
<use href="#foot_left_base" xlink:href="#foot_left_base"/>
</clipPath>
<clipPath id="clip_foot_right">
<use href="#foot_right_base" xlink:href="#foot_right_base"/>
</clipPath>
<clipPath id="clip_wing_tip_right">
<use href="#wing_tip_right_base" xlink:href="#wing_tip_right_base"/>
</clipPath>
<clipPath id="clip_eye_left">
<use href="#eyeball_left" xlink:href="#eyeball_left"/>
</clipPath>
<clipPath id="clip_pupil_left">
<use href="#pupil_left_base" xlink:href="#pupil_left_base"/>
</clipPath>
<clipPath id="clip_eye_right">
<use href="#eyeball_right" xlink:href="#eyeball_right"/>
</clipPath>
<clipPath id="clip_pupil_right">
<use href="#pupil_right_base" xlink:href="#pupil_right_base"/>
</clipPath>
<clipPath id="clip_mandible_lower">
<use href="#mandible_lower_base" xlink:href="#mandible_lower_base"/>
</clipPath>
<clipPath id="clip_mandible_upper">
<use href="#mandible_upper_base" xlink:href="#mandible_upper_base"/>
</clipPath>
<clipPath id="clip_beak">
<use href="#mandible_lower_base" xlink:href="#mandible_lower_base"/>
<use href="#mandible_upper_base" xlink:href="#mandible_upper_base"/>
</clipPath>
</defs>
<g id="tux" transform="translate(20)">
<g id="body">
<path id="body_base" d="m106.95 0c-6 0-12.02 1.18-17.46 4.12-5.78 3.11-10.52 8.09-13.43 13.97-2.92 5.88-4.06 12.16-4.24 19.08-0.33 13.14 0.3 26.92 1.29 39.41 0.26 3.8 0.74 6.02 0.25 9.93-1.62 8.3-8.88 13.88-12.76 21.17-4.27 8.04-6.07 17.13-9.29 25.65-2.95 7.79-7.09 15.1-9.88 22.95-3.91 10.97-5.08 23.03-2.5 34.39 1.97 8.66 6.08 16.78 11.62 23.73-0.8 1.44-1.58 2.91-2.4 4.34-2.57 4.43-5.71 8.64-7.17 13.55-0.73 2.45-1.02 5.07-0.55 7.59s1.75 4.93 3.75 6.53c1.31 1.04 2.9 1.72 4.53 2.1 1.63 0.37 3.32 0.46 5 0.43 6.37-0.14 12.55-2.07 18.71-3.69 3.66-0.96 7.34-1.81 11.03-2.58 13.14-2.69 27.8-1.61 39.99 0.15 4.13 0.63 8.23 1.44 12.29 2.43 6.36 1.54 12.69 3.5 19.23 3.69 1.72 0.05 3.46-0.03 5.14-0.4 1.68-0.38 3.31-1.06 4.65-2.13 2.01-1.6 3.29-4.02 3.76-6.54s0.18-5.15-0.56-7.61c-1.48-4.92-4.65-9.11-7.27-13.52-1.04-1.75-2-3.53-3.03-5.28 7.9-8.87 14.26-19.13 17.94-30.4 4.01-12.3 4.75-25.55 3.06-38.38s-5.76-25.27-11.11-37.05c-6.72-14.76-12.37-20.1-16.47-33.07-4.42-14.02-0.77-30.61-4.06-43.32-1.17-4.32-3.04-8.45-5.45-12.23-2.82-4.43-6.4-8.39-10.65-11.47-6.78-4.92-15.3-7.54-23.96-7.54z" fill="#020204"/>
<path id="belly" d="m83.13 74c-0.9 1.13-1.48 2.49-1.84 3.89-0.35 1.4-0.48 2.85-0.54 4.3-0.11 2.89 0.07 5.83-0.7 8.62-0.82 2.98-2.65 5.57-4.44 8.08-3.11 4.36-6.25 8.84-7.78 13.97-0.93 3.1-1.24 6.39-0.91 9.62-3.47 5.1-6.48 10.53-8.98 16.18-3.78 8.57-6.37 17.69-7.28 27.01-1.12 11.41 0.34 23.15 4.85 33.69 3.25 7.63 8.11 14.6 14.38 20.04 3.18 2.76 6.72 5.11 10.5 6.97 13.11 6.45 29.31 6.46 42.2-0.41 6.74-3.59 12.43-8.84 17.91-14.15 3.3-3.2 6.59-6.48 9.11-10.32 4.85-7.41 6.54-16.41 7.59-25.2 1.83-15.36 1.89-31.6-4.85-45.53-2.32-4.8-5.41-9.22-9.12-13.05-0.98-6.7-2.93-13.27-5.76-19.42-2.05-4.45-4.54-8.68-6.44-13.18-0.78-1.85-1.46-3.75-2.32-5.56-0.87-1.81-1.93-3.55-3.39-4.94-1.48-1.42-3.33-2.43-5.28-3.07-1.95-0.65-4.01-0.94-6.06-1.04-4.11-0.21-8.22 0.33-12.33 0.16-3.27-0.13-6.53-0.7-9.8-0.51-1.63 0.1-3.26 0.39-4.78 1.01-1.52 0.61-2.92 1.56-3.94 2.84z" fill="#fdfdfb"/>
<g id="body_self_shadows">
<path id="belly_shadow_left" d="m68.67 115.18c0.87 1.31-0.55 5.84 19.86 2.94 0 0-3.59 0.39-7.12 1.21-5.49 1.84-10.27 3.89-13.97 6.61-3.65 2.7-6.33 6.21-9.68 9.22 0 0 5.43-9.92 6.78-12.91 1.36-2.99-0.22-2.85 0.85-7.25s3.69-8.63 3.69-8.63-2.14 6.22-0.41 8.81z" clip-path="url(#clip_body)" fill="url(#fill_belly_shadow_left)" filter="url(#blur_belly_shadow_left)" opacity=".25"/>
<path id="belly_shadow_right" d="m134.28 113.99c-4.16 2.9-6.6 2.56-11.64 3.12-5.05 0.57-18.7 0.36-18.7 0.36s1.97-0.03 6.36 0.78c4.38 0.82 13.31 1.6 18.34 3.51 5.04 1.92 6.87 2.47 9.93 4.4 4.35 2.75 7.55 7.06 11.71 10.08 0 0 0.2-4-1.48-6.99s-6.2-7.7-7.53-12.1c-1.32-4.4-1.96-13.04-1.96-13.04s-0.88 6.99-5.03 9.88z" clip-path="url(#clip_body)" fill="url(#fill_belly_shadow_right)" filter="url(#blur_belly_shadow_right)" opacity=".42"/>
<path id="belly_shadow_middle" d="m95.17 107.81c-0.16 1.25-0.36 2.5-0.6 3.74-0.12 0.61-0.26 1.22-0.48 1.8-0.23 0.58-0.56 1.14-1.02 1.55-0.41 0.37-0.9 0.62-1.4 0.85-1.94 0.88-4.01 1.47-6.12 1.74 0.84 0.06 1.68 0.14 2.53 0.23 0.53 0.06 1.06 0.12 1.57 0.25 0.52 0.14 1.03 0.34 1.46 0.65 0.47 0.35 0.84 0.82 1.12 1.34 0.55 1.02 0.73 2.2 0.83 3.37 0.13 1.48 0.14 2.98 0.03 4.46 0.1-0.99 0.31-1.98 0.62-2.92 0.57-1.72 1.47-3.32 2.69-4.65 0.49-0.52 1.02-1.01 1.6-1.42 1.79-1.26 4.07-1.81 6.24-1.51-2.21 0.09-4.44-0.6-6.2-1.93-0.9-0.68-1.68-1.52-2.22-2.5-0.84-1.52-1.08-3.37-0.65-5.05z" clip-path="url(#clip_body)" fill="url(#fill_belly_shadow_middle)" filter="url(#blur_belly_shadow_middle)" opacity=".2"/>
<path id="belly_shadow_lower" d="m89.85 137.14c-1.06 4.03-1.79 8.15-2.17 12.31-0.55 5.87-0.42 11.78-0.74 17.67-0.26 4.99-0.85 10.04 0.02 14.97 0.41 2.35 1.15 4.64 2.2 6.78 0.16-0.82 0.29-1.64 0.36-2.47 0.37-4-0.3-8.01-0.53-12.01-0.4-7.02 0.57-14.04 0.97-21.06 0.3-5.39 0.27-10.8-0.11-16.19z" clip-path="url(#clip_body)" filter="url(#blur_belly_shadow_lower)" opacity=".11"/>
</g>
<g id="body_glare">
<path id="belly_glare" d="m160.08 131.23c1.03-0.16 7.34 5.21 6.48 7.21-0.86 1.99-2.49 0.79-3.65 0.8-1.16 0.02-4.33 1.46-4.86 0.55-0.54-0.91 1.4-3.03 2.41-4.81 0.82-1.43-1.4-3.59-0.38-3.75z" clip-path="url(#clip_body)" fill="#7c7c7c" filter="url(#blur_belly_glare)" opacity=".75"/>
<path id="head_glare" d="m121.52 11.12c-2.21 1.56-1.25 3.51-0.3 5.46 0.95 1.96-2.09 7.59-2.12 7.83s5.98-2.85 7.62-4.87c1.94-2.37 6.83 3.22 6.56 2.37 0.01-1.52-9.55-12.34-11.76-10.79z" clip-path="url(#clip_body)" fill="#7c7c7c" filter="url(#blur_head_glare)"/>
<path id="neck_glare" d="m138.27 76.63c-1.86 1.7 0.88 4.25 2.17 7.24 0.81 1.86 3.04 4.49 5.2 4.07 1.63-0.32 2.63-2.66 2.48-4.3-0.3-3.18-2.98-3.93-4.93-5.02-1.54-0.86-3.61-3.18-4.92-1.99z" clip-path="url(#clip_body)" fill="#838384" filter="url(#blur_neck_glare)"/>
</g>
</g>
<g id="wings">
<g id="wing_left">
<path id="wing_left_base" d="m63.98 100.91c-6.1 6.92-12.37 13.63-15.81 21.12-1.71 3.8-2.51 7.93-3.68 11.93-1.32 4.54-3.12 8.94-5.14 13.22-1.87 3.95-3.93 7.81-5.98 11.66-1.5 2.81-3.02 5.67-3.54 8.81-0.41 2.48-0.18 5.04 0.46 7.47 0.63 2.43 1.64 4.75 2.79 6.98 4.88 9.55 12.21 17.77 20.89 24.07 3.94 2.85 8.15 5.32 12.58 7.35 2.4 1.09 4.92 2.07 7.56 2.11 1.32 0.03 2.65-0.19 3.86-0.72 1.2-0.53 2.28-1.38 3-2.49 0.88-1.36 1.18-3.05 1-4.66s-0.81-3.15-1.65-4.53c-2.06-3.38-5.31-5.83-8.44-8.25-6.76-5.23-13.29-10.76-19.55-16.58-1.76-1.65-3.53-3.34-4.76-5.42-1.2-2.02-1.85-4.32-2.29-6.63-1.21-6.33-0.9-12.99 1.25-19.07 0.85-2.38 1.96-4.65 3.04-6.93 1.86-3.95 3.62-7.98 6.07-11.6 3.05-4.51 7.13-8.33 9.61-13.17 2.1-4.09 2.95-8.68 3.76-13.2 0.64-3.54 1.85-7 2.47-10.54-1.21 2.3-5.11 6.07-7.5 9.07z" fill="#020204"/>
<path id="wing_left_glare" d="m56.96 126.1c-2 1.84-3.73 3.97-5.13 6.31-2.3 3.84-3.65 8.16-5.33 12.31-1.24 3.09-2.69 6.2-2.86 9.53-0.09 1.71 0.16 3.42 0.22 5.13s-0.1 3.49-0.94 4.98c-0.7 1.25-1.87 2.23-3.22 2.71 1.83 0.61 3.45 1.79 4.6 3.33 0.96 1.3 1.58 2.81 2.41 4.18 0.68 1.12 1.51 2.16 2.54 2.97 1.02 0.82 2.25 1.4 3.54 1.56 1.79 0.23 3.65-0.36 4.97-1.58-1.66-15.55-0.14-31.42 4.44-46.37 0.29-0.94 0.59-1.89 0.67-2.87 0.07-0.99-0.12-2.03-0.72-2.81-0.31-0.42-0.74-0.75-1.23-0.96-0.48-0.2-1.02-0.28-1.54-0.21-0.52 0.06-1.03 0.26-1.45 0.57-0.42 0.32-0.76 0.74-0.97 1.22z" clip-path="url(#clip_wing_left)" fill="#7c7c7c" filter="url(#blur_wing_left_glare)" opacity=".95"/>
</g>
<g id="wing_right">
<path id="wing_right_base" d="m162.76 127.12c5.24 4.22 8.57 10.59 9.6 17.24 0.8 5.18 0.28 10.51-0.89 15.62-1.17 5.12-2.97 10.06-4.77 15-0.71 1.96-1.43 3.95-1.71 6.02-0.29 2.08-0.11 4.27 0.89 6.11 1.15 2.11 3.29 3.56 5.59 4.24 2.27 0.68 4.72 0.66 7.02 0.09s6.17-1.31 8.04-2.77c4.75-3.69 5.88-10.1 7.01-15.72 1.17-5.87 0.6-12.02-0.43-17.95-1.41-8.09-3.78-15.99-6.79-23.62-2.22-5.62-5.06-10.98-8.44-15.96-3.32-4.89-8.02-8.7-11.5-13.48-1.21-1.66-2.66-3.38-3.84-5.06-2.56-3.62-1.98-2.94-3.57-5.29-1.15-1.7-2.97-2.28-4.88-3.02-1.92-0.74-4.06-0.96-6.04-0.41-2.6 0.73-4.73 2.79-5.86 5.24-1.13 2.46-1.33 5.28-0.89 7.95 0.57 3.44 2.14 6.64 3.92 9.64 2 3.39 4.32 6.66 7.35 9.18 3.16 2.63 6.98 4.37 10.19 6.95z" fill="#020204"/>
<path id="wing_right_glare" d="m150.42 118.99c0.42 0.4 0.86 0.81 1.31 1.19 3.22 2.63 4.93 5.58 8.2 8.16 5.34 4.22 10.75 11.5 11.8 18.15 0.82 5.19-0.26 8.01-1.58 14.12-1.32 6.12-5.06 14.78-7.09 20.68-0.8 2.35 1.64 1.38 1.32 3.86-0.16 1.22-0.18 2.45-0.03 3.67 0.02-0.23 0.03-0.48 0.06-0.71 0.39-3.38 1.42-6.63 2.55-9.82 2.17-6.13 4.66-12.15 6.38-18.45 1.72-6.29 1.53-10.82 0.63-16.23-1.13-6.81-5.09-13.09-10.69-17.24-3.97-2.93-8.64-4.81-12.86-7.38z" clip-path="url(#clip_wing_right)" fill="#838384" filter="url(#blur_wing_right_glare)"/>
</g>
</g>
<g id="feet">
<g id="foot_left">
<path id="foot_left_base" d="m34.98 175.33c1.38-0.57 2.93-0.68 4.39-0.41 1.47 0.27 2.86 0.91 4.09 1.74 2.47 1.68 4.3 4.12 6.05 6.54 4.03 5.54 7.9 11.2 11.42 17.08 2.85 4.78 5.46 9.71 8.76 14.18 2.15 2.93 4.57 5.64 6.73 8.55 2.16 2.92 4.07 6.08 5.03 9.58 1.25 4.55 0.76 9.56-1.4 13.75-1.52 2.95-3.86 5.48-6.7 7.19s-5.83 2.47-9.15 2.47c-5.27 0-10.42-2.83-15.32-4.78-9.98-3.98-20.82-5.22-31.11-8.32-3.16-0.95-6.27-2.08-9.45-2.95-1.42-0.39-2.85-0.73-4.19-1.34-1.34-0.6-2.59-1.51-3.33-2.77-0.57-0.98-0.8-2.13-0.8-3.26 0-1.14 0.28-2.26 0.67-3.32 0.77-2.13 2.02-4.06 2.86-6.17 1.37-3.44 1.62-7.23 1.43-10.93-0.18-3.69-0.78-7.36-1.03-11.05-0.12-1.65-0.16-3.32 0.16-4.95 0.31-1.62 1.01-3.21 2.2-4.35 1.1-1.06 2.55-1.69 4.05-2 1.49-0.31 3.03-0.32 4.55-0.29s3.05 0.12 4.57-0.01c1.52-0.12 3.05-0.46 4.37-1.22 1.26-0.72 2.29-1.79 3.14-2.96s1.54-2.45 2.25-3.72c0.7-1.26 1.43-2.52 2.36-3.64 0.92-1.12 2.06-2.09 3.4-2.64z" fill="url(#fill_foot_left_base)"/>
<path d="m37.16 177.7c1.25-0.5 2.67-0.56 3.98-0.26 1.32 0.3 2.55 0.94 3.61 1.77 2.14 1.65 3.62 3.97 5.05 6.26 3.42 5.54 6.76 11.15 9.92 16.86 2.4 4.31 4.68 8.7 7.62 12.65 1.95 2.62 4.18 5.03 6.17 7.62s3.76 5.41 4.64 8.56c1.14 4.05 0.68 8.54-1.28 12.26-1.42 2.68-3.58 4.96-6.2 6.48-2.61 1.52-5.67 2.28-8.69 2.14-4.82-0.22-9.23-2.63-13.77-4.26-8.71-3.16-18.14-3.59-27.08-6.05-3.2-0.87-6.32-2.03-9.53-2.84-1.43-0.36-2.88-0.66-4.23-1.23s-2.62-1.45-3.36-2.72c-0.54-0.95-0.76-2.06-0.73-3.15 0.04-1.09 0.31-2.17 0.7-3.19 0.78-2.04 2-3.88 2.78-5.92 1.19-3.08 1.34-6.47 1.12-9.76s-0.8-6.56-1-9.85c-0.08-1.48-0.1-2.97 0.2-4.41 0.3-1.45 0.93-2.85 1.98-3.89 1.14-1.13 2.7-1.74 4.29-1.99 1.58-0.24 3.19-0.13 4.78 0.01 1.6 0.14 3.2 0.32 4.8 0.23 1.6-0.1 3.22-0.49 4.54-1.39 1.2-0.81 2.1-2 2.79-3.27s1.18-2.64 1.71-3.98c0.52-1.35 1.09-2.69 1.91-3.89 0.82-1.19 1.93-2.24 3.28-2.79z" clip-path="url(#clip_foot_left)" fill="#d99a03" filter="url(#blur_foot_left_layer_1)"/>
<path d="m35.99 174.57c1.22-0.6 2.65-0.72 3.98-0.45s2.57 0.92 3.62 1.77c2.09 1.7 3.43 4.13 4.67 6.51 2.84 5.46 5.5 11.04 8.9 16.19 2.48 3.73 5.33 7.2 7.83 10.92 3.39 5.03 6.15 10.57 7.29 16.5 0.76 4 0.74 8.31-1.18 11.9-1.27 2.37-3.32 4.31-5.75 5.52-2.42 1.22-5.21 1.71-7.92 1.47-4.27-0.37-8.14-2.47-12.16-3.94-7.13-2.59-14.84-3.22-22.18-5.18-3.09-0.82-6.13-1.89-9.26-2.54-1.39-0.29-2.8-0.5-4.12-1s-2.57-1.33-3.25-2.55c-0.47-0.86-0.63-1.86-0.56-2.84 0.07-0.97 0.36-1.92 0.74-2.83 0.77-1.8 1.9-3.46 2.49-5.32 0.88-2.75 0.52-5.72-0.14-8.53-0.65-2.8-1.6-5.55-1.89-8.41-0.13-1.27-0.13-2.57 0.17-3.82 0.29-1.25 0.88-2.45 1.81-3.34 1.2-1.15 2.88-1.73 4.56-1.89 1.67-0.16 3.35 0.06 5.01 0.3s3.34 0.5 5.01 0.42c1.68-0.07 3.39-0.51 4.7-1.54 1.3-1.02 2.12-2.53 2.59-4.09 0.47-1.57 0.62-3.2 0.81-4.82s0.43-3.26 1.06-4.77 1.69-2.9 3.17-3.64z" clip-path="url(#clip_foot_left)" fill="#f5bd0c" filter="url(#blur_foot_left_layer_2)"/>
<path id="foot_left_glare" d="m51.2 188.21c2.25 4.06 3.62 8.72 5.85 12.82 2.05 3.77 4.38 7.65 6.46 11.12 0.93 1.55 3.09 3.93 5.27 7.62 1.98 3.34 3.98 8.01 5.1 9.58-0.64-1.84-1.96-6.77-3.54-10.28-1.47-3.28-3.19-5.15-4.24-6.92-2.08-3.47-4.33-6.6-6.47-9.91-2.95-4.57-5.2-9.68-8.43-14.03z" clip-path="url(#clip_foot_left)" fill="url(#fill_foot_left_glare)" filter="url(#blur_foot_left_glare)"/>
</g>
<g id="foot_right">
<path id="foot_right_shadow" d="m198.7 215.61c-0.4 1.33-1.02 2.62-1.81 3.8-1.75 2.59-4.3 4.55-6.84 6.35-4.33 3.07-8.85 5.89-12.89 9.38-2.7 2.34-5.17 4.97-7.45 7.73-1.95 2.36-3.79 4.84-6.02 6.94-2.25 2.12-4.89 3.84-7.74 4.77-3.47 1.13-7.13 1.08-10.47 0.22-2.34-0.6-4.63-1.64-6.08-3.53s-1.92-4.44-2.09-6.94c-0.3-4.42 0.23-8.93 0.71-13.42 0.4-3.73 0.77-7.46 0.92-11.18 0.27-6.77-0.18-13.47-1.09-20.05-0.16-1.11-0.32-2.22-0.23-3.35 0.09-1.14 0.47-2.32 1.27-3.2 0.74-0.81 1.77-1.29 2.79-1.52 1.02-0.24 2.06-0.25 3.09-0.28 2.43-0.06 4.86-0.21 7.25 0.01 1.51 0.13 2.99 0.41 4.49 0.55 2.51 0.24 5.12 0.12 7.64-0.62 2.71-0.8 5.29-2.29 8.05-2.7 1.13-0.17 2.26-0.15 3.36 0.01 1.12 0.15 2.24 0.46 3.1 1.15 0.66 0.52 1.14 1.23 1.51 1.99 0.56 1.14 0.9 2.39 1.1 3.68 0.17 1.14 0.24 2.31 0.53 3.41 0.48 1.81 1.58 3.35 2.89 4.6 1.32 1.25 2.85 2.24 4.39 3.22 1.53 0.97 3.07 1.93 4.7 2.73 0.77 0.38 1.56 0.72 2.29 1.15 0.74 0.44 1.42 0.97 1.91 1.67 0.66 0.95 0.92 2.2 0.72 3.43z" clip-path="url(#clip_body)" fill="url(#fill_foot_right_shadow)" filter="url(#blur_foot_right_shadow)" opacity=".2"/>
<path id="foot_right_base" d="m213.47 222.92c-2.26 2.68-5.4 4.45-8.53 6.05-5.33 2.71-10.86 5.1-15.87 8.37-3.36 2.19-6.46 4.76-9.36 7.53-2.48 2.37-4.83 4.9-7.61 6.91-2.81 2.03-6.05 3.5-9.48 4.01-0.95 0.14-1.9 0.21-2.86 0.21-3.24 0-6.48-0.78-9.46-2.08-2.7-1.17-5.3-2.86-6.86-5.36-1.56-2.52-1.92-5.59-1.92-8.56-0.01-5.23 0.96-10.41 1.87-15.57 0.76-4.29 1.48-8.58 1.95-12.91 0.85-7.86 0.84-15.81 0.28-23.71-0.1-1.32-0.21-2.65-0.01-3.96s0.74-2.62 1.74-3.48c0.93-0.8 2.17-1.16 3.4-1.22 1.22-0.07 2.44 0.12 3.65 0.3 2.85 0.42 5.73 0.74 8.52 1.48 1.76 0.46 3.48 1.08 5.23 1.56 2.94 0.79 6.01 1.17 9.02 0.82 3.25-0.38 6.41-1.6 9.68-1.52 1.34 0.03 2.67 0.28 3.95 0.69 1.3 0.41 2.59 1 3.55 1.98 0.73 0.74 1.24 1.67 1.62 2.64 0.57 1.44 0.88 2.98 1.01 4.52 0.11 1.37 0.09 2.76 0.35 4.11 0.43 2.21 1.6 4.24 3.04 5.97 1.45 1.74 3.18 3.21 4.91 4.66s3.46 2.89 5.32 4.16c0.87 0.6 1.77 1.16 2.6 1.81 0.83 0.66 1.59 1.42 2.11 2.34 0.45 0.81 0.69 1.72 0.69 2.65 0 0.52-0.07 1.04-0.23 1.56-0.45 1.43-1.28 2.82-2.3 4.04z" fill="url(#fill_foot_right_base)"/>
<path d="m213.21 216.12c-0.53 1.33-1.28 2.58-2.22 3.67-2.07 2.42-4.93 4.01-7.78 5.44-4.88 2.44-9.92 4.58-14.5 7.52-3.06 1.97-5.9 4.28-8.55 6.78-2.26 2.13-4.41 4.41-6.95 6.21-2.57 1.83-5.53 3.14-8.65 3.6-3.8 0.56-7.72-0.16-11.25-1.67-2.46-1.06-4.84-2.56-6.27-4.83-1.42-2.26-1.75-5.02-1.75-7.69-0.02-4.71 0.87-9.37 1.71-14 0.7-3.85 1.36-7.71 1.78-11.6 0.76-7.08 0.73-14.22 0.25-21.32-0.08-1.19-0.17-2.39 0.01-3.57s0.67-2.35 1.57-3.13c0.85-0.73 1.99-1.05 3.11-1.1 1.11-0.06 2.22 0.12 3.33 0.28 2.61 0.38 5.23 0.67 7.78 1.33 1.61 0.42 3.18 0.98 4.78 1.4 2.68 0.72 5.49 1.06 8.24 0.74 2.97-0.34 5.85-1.44 8.83-1.37 1.23 0.03 2.44 0.26 3.61 0.62 1.19 0.37 2.37 0.9 3.25 1.78 0.66 0.67 1.11 1.51 1.48 2.38 0.53 1.29 0.89 2.67 0.91 4.07 0.03 1.46-0.28 2.92-0.09 4.37 0.16 1.17 0.66 2.28 1.3 3.28 0.63 1 1.4 1.91 2.17 2.81 1.48 1.75 2.96 3.53 4.82 4.87 2.11 1.53 4.62 2.43 6.8 3.85 0.65 0.43 1.28 0.91 1.74 1.54 0.78 1.06 0.98 2.5 0.54 3.74z" clip-path="url(#clip_foot_right)" fill="#cd8907" filter="url(#blur_foot_right_layer_1)"/>
<path d="m212.91 214.61c-0.6 1.35-1.37 2.6-2.28 3.71-2.12 2.58-4.99 4.35-8 5.49-4.97 1.88-10.39 2.13-15.26 4.27-2.97 1.3-5.65 3.26-8.36 5.12-2.18 1.49-4.42 2.94-6.82 3.98-2.72 1.19-5.6 1.85-8.5 2.32-1.84 0.29-3.71 0.51-5.57 0.41s-3.72-0.54-5.37-1.49c-1.24-0.72-2.36-1.75-3.03-3.1-0.73-1.49-0.86-3.24-0.85-4.94 0.05-4.5 1.02-8.96 0.99-13.47-0.03-3.93-0.81-7.8-1.03-11.72-0.43-7.54 1.19-15.2-0.24-22.59-0.22-1.19-0.53-2.37-0.52-3.58 0.01-0.6 0.1-1.21 0.31-1.77 0.22-0.55 0.56-1.06 1.01-1.42 0.39-0.29 0.84-0.47 1.31-0.56 0.46-0.08 0.94-0.06 1.41 0.01 0.93 0.15 1.82 0.51 2.73 0.78 2.6 0.78 5.35 0.76 8 1.35 1.66 0.36 3.26 0.97 4.91 1.41 2.75 0.76 5.63 1.08 8.46 0.75 3.04-0.36 6.01-1.46 9.07-1.38 1.26 0.03 2.5 0.26 3.71 0.62s2.42 0.87 3.34 1.8c0.65 0.67 1.13 1.52 1.51 2.4 0.57 1.29 0.96 2.69 0.95 4.11-0.01 0.74-0.12 1.47-0.19 2.21-0.06 0.74-0.08 1.49 0.09 2.2 0.18 0.72 0.55 1.37 0.97 1.96s0.9 1.12 1.34 1.7c1.22 1.61 2.1 3.49 3.05 5.3s2.02 3.6 3.53 4.91c2.05 1.77 4.7 2.48 6.99 3.89 0.67 0.41 1.31 0.89 1.78 1.55 0.38 0.52 0.63 1.15 0.73 1.81 0.09 0.65 0.03 1.34-0.17 1.96z" clip-path="url(#clip_foot_right)" fill="#f5c021" filter="url(#blur_foot_right_layer_2)"/>
<path id="foot_right_glare" d="m148.08 181.58c2.82-0.76 5.22 1.38 7.27 2.99 1.32 1.13 3.24 0.85 4.86 0.9 2.69-0.09 5.36 0.45 8.05 0.12 5.3-0.45 10.49-1.75 15.81-1.97 2.54-0.16 5.4-0.31 7.59 1.17 0.89 0.62 2.2 3.23 3.07 2.25-0.36-2.74-2.39-5.39-5.11-6.12-2.14-0.34-4.3 0.25-6.46 0.06-6.39-0.15-12.75-1.34-19.16-1-4.46 0.04-8.91-0.17-13.37-0.34-1.75-0.36-2.37 1.19-3.32 1.79 0.25 0.19 0.34 0.25 0.77 0.15z" clip-path="url(#clip_foot_right)" fill="url(#fill_foot_right_glare)" filter="url(#blur_foot_right_glare)"/>
</g>
</g>
<g id="wing_tip_right">
<g id="wing_tip_right_shadow">
<path id="wing_tip_right_shadow_lower" d="m185.49 187.61c-0.48-0.95-1.36-1.66-2.35-2.07-0.98-0.41-2.06-0.55-3.13-0.54-2.13 0.02-4.25 0.57-6.38 0.39-1.79-0.16-3.49-0.83-5.24-1.26-1.81-0.44-3.73-0.61-5.52-0.12-1.92 0.52-3.61 1.81-4.67 3.49-0.94 1.48-1.38 3.23-1.52 4.98s0.01 3.5 0.19 5.25c0.12 1.26 0.27 2.52 0.57 3.75 0.31 1.23 0.78 2.43 1.52 3.46 1.07 1.48 2.66 2.54 4.37 3.17 2.8 1.03 5.98 0.98 8.73-0.15 4.88-2.12 9.01-5.92 11.52-10.6 0.91-1.68 1.61-3.47 2.06-5.31 0.18-0.74 0.32-1.49 0.32-2.25 0.01-0.75-0.12-1.52-0.47-2.19z" clip-path="url(#clip_foot_right)" fill="url(#fill_wing_tip_right_shadow_lower)" filter="url(#blur_wing_tip_right_shadow_lower)" opacity=".35"/>
<path id="wing_tip_right_shadow_upper" d="m185.49 184.89c-0.48-0.69-1.36-1.2-2.35-1.5-0.98-0.3-2.06-0.39-3.13-0.39-2.13 0.02-4.25 0.42-6.38 0.28-1.79-0.11-3.49-0.6-5.24-0.9-1.81-0.32-3.73-0.45-5.52-0.09-1.92 0.37-3.61 1.3-4.67 2.52-0.94 1.07-1.38 2.34-1.52 3.6s0.01 2.53 0.19 3.79c0.12 0.91 0.27 1.83 0.57 2.72 0.31 0.89 0.78 1.76 1.52 2.5 1.07 1.07 2.66 1.83 4.37 2.29 2.8 0.75 5.98 0.71 8.73-0.11 4.88-1.53 9.01-4.28 11.52-7.66 0.91-1.22 1.61-2.51 2.06-3.84 0.18-0.54 0.32-1.08 0.32-1.62 0.01-0.55-0.12-1.11-0.47-1.59z" clip-path="url(#clip_foot_right)" fill="url(#fill_wing_tip_right_shadow_upper)" filter="url(#blur_wing_tip_right_shadow_upper)" opacity=".35"/>
</g>
<path id="wing_tip_right_base" d="m189.55 178.72c-0.35-0.95-0.97-1.79-1.72-2.47s-1.64-1.2-2.57-1.6c-1.86-0.79-3.89-1.09-5.89-1.46-1.87-0.35-3.74-0.78-5.62-1.1-1.96-0.33-3.98-0.55-5.92-0.11-1.69 0.38-3.26 1.26-4.54 2.43s-2.28 2.63-3 4.21c-1.27 2.79-1.67 5.92-1.43 8.97 0.18 2.27 0.76 4.61 2.25 6.32 1.21 1.39 2.92 2.26 4.68 2.78 3.04 0.9 6.35 0.85 9.36-0.13 4.97-1.67 9.37-4.98 12.35-9.29 0.98-1.43 1.82-2.98 2.2-4.66 0.29-1.28 0.3-2.66-0.15-3.89z" fill="#020204"/>
<g id="wing_tip_right_glare">
<defs>
<path id="path_wing_tip_right_glare" d="m168.89 171.07c-0.47 0.03-0.93 0.08-1.4 0.17-2.99 0.53-5.73 2.42-7.27 5.03-1.09 1.85-1.58 4.03-1.43 6.17 0.07-1.5 0.46-2.97 1.19-4.28 1.23-2.23 3.47-3.91 5.98-4.37 1.54-0.28 3.13-0.11 4.68 0.08 1.5 0.19 3 0.39 4.47 0.7 2.28 0.5 4.53 1.26 6.44 2.59 0.44 0.31 0.86 0.66 1.21 1.08 0.35 0.41 0.62 0.89 0.73 1.42 0.15 0.78-0.07 1.6-0.46 2.29-0.39 0.7-0.92 1.3-1.48 1.86-0.46 0.46-0.94 0.89-1.43 1.32 2.21-0.43 4.44-1.03 6.28-2.31 0.77-0.55 1.48-1.2 1.94-2.02 0.46-0.83 0.65-1.83 0.43-2.75-0.16-0.62-0.5-1.19-0.92-1.67s-0.93-0.87-1.45-1.24c-2.31-1.62-5.01-2.65-7.81-2.99-1.8-0.33-3.61-0.61-5.42-0.83-1.41-0.18-2.86-0.33-4.28-0.25z"/>
</defs>
<use clip-path="url(#clip_wing_tip_right)" fill="url(#fill_wing_tip_right_glare_1)" filter="url(#blur_wing_tip_right_glare)" href="#path_wing_tip_right_glare" xlink:href="#path_wing_tip_right_glare"/>
<use clip-path="url(#clip_wing_tip_right)" fill="url(#fill_wing_tip_right_glare_2)" filter="url(#blur_wing_tip_right_glare)" href="#path_wing_tip_right_glare" xlink:href="#path_wing_tip_right_glare"/>
</g>
</g>
<g id="face">
<g id="eyes">
<g id="eye_left">
<path id="eyeball_left" d="m84.45 38.28c-1.53 0.08-3 0.79-4.12 1.84-1.13 1.05-1.92 2.43-2.41 3.88-0.97 2.92-0.75 6.08-0.53 9.15 0.2 2.77 0.41 5.6 1.45 8.18 0.52 1.3 1.25 2.51 2.22 3.51 0.97 0.99 2.2 1.76 3.55 2.09 1.26 0.32 2.62 0.26 3.86-0.13 1.25-0.4 2.38-1.11 3.32-2.02 1.36-1.33 2.27-3.07 2.8-4.9s0.68-3.75 0.65-5.66c-0.04-2.38-0.35-4.77-1.09-7.03-0.75-2.26-1.94-4.4-3.6-6.11-0.8-0.83-1.72-1.55-2.75-2.06-1.04-0.51-2.2-0.8-3.35-0.74z" fill="url(#fill_eyeball_left)"/>
<g id="pupil_left">
<path id="pupil_left_base" d="m80.75 50.99c-0.32 1.94-0.33 3.97 0.33 5.81 0.44 1.22 1.17 2.33 2.05 3.28 0.57 0.62 1.23 1.18 1.99 1.55 0.77 0.37 1.65 0.52 2.48 0.32 0.76-0.19 1.42-0.68 1.91-1.29s0.82-1.34 1.05-2.09c0.69-2.21 0.58-4.62-0.11-6.83-0.49-1.61-1.32-3.16-2.6-4.24-0.62-0.52-1.34-0.93-2.12-1.11-0.78-0.19-1.63-0.14-2.36 0.19-0.81 0.37-1.44 1.07-1.85 1.86s-0.62 1.67-0.77 2.55z" fill="#020204"/>
<path id="pupil_left_glare" d="m84.84 49.59c0.21 0.55 0.91 0.75 1.3 1.19 0.37 0.42 0.76 0.87 0.97 1.4 0.39 1.01-0.39 2.51 0.43 3.23 0.25 0.22 0.77 0.23 1.02 0 0.99-0.9 0.77-2.71 0.38-3.99-0.36-1.15-1.23-2.25-2.31-2.8-0.5-0.26-1.25-0.47-1.68-0.11-0.27 0.24-0.24 0.74-0.11 1.08z" clip-path="url(#clip_pupil_left)" fill="url(#fill_pupil_left_glare)" filter="url(#blur_pupil_left_glare)"/>
</g>
<path id="eyelid_left" d="m81.14 44.46c2.32-1.38 5.13-1.7 7.82-1.45 2.68 0.26 5.27 1.04 7.87 1.75 1.91 0.52 3.84 1 5.63 1.84 1.78 0.84 3.44 2.08 4.43 3.8 0.16 0.27 0.29 0.56 0.46 0.83s0.37 0.52 0.62 0.71 0.57 0.32 0.88 0.3c0.16-0.01 0.32-0.05 0.45-0.13 0.14-0.08 0.26-0.2 0.33-0.34 0.08-0.16 0.11-0.35 0.1-0.53s-0.05-0.36-0.1-0.54c-0.65-2.37-2.19-4.38-3.35-6.55-0.7-1.3-1.28-2.66-1.98-3.96-2.43-4.45-6.42-7.94-10.95-10.21s-9.59-3.36-14.65-3.65c-5.86-0.35-11.73 0.35-17.51 1.37-2.51 0.44-5.06 0.96-7.27 2.21-1.11 0.62-2.13 1.42-2.92 2.42-0.8 0.99-1.36 2.18-1.55 3.44-0.17 1.22 0.01 2.47 0.44 3.62 0.42 1.15 1.08 2.2 1.86 3.15 1.54 1.91 3.53 3.39 5.36 5.03 1.83 1.63 3.52 3.44 5.57 4.79 1.02 0.68 2.13 1.24 3.31 1.57s2.44 0.42 3.64 0.17c1.24-0.25 2.4-0.86 3.41-1.64 1.01-0.77 1.88-1.7 2.71-2.66 1.66-1.93 3.21-4.04 5.39-5.34z" clip-path="url(#clip_eye_left)" fill="url(#fill_eyelid_left)"/>
<path id="eyebrow_left" d="m90.77 36.57c2.16 2.02 3.76 4.52 4.85 7.16-0.48-2.91-1.23-5.26-3.13-7.16-1.16-1.09-2.49-2.05-3.98-2.72-1.32-0.59-2.77-0.96-3.61-0.97-0.83-0.02-1.03 0-1.2 0.01-0.18 0.01-0.31 0.01 0.23 0.08 0.54 0.06 1.75 0.39 3.05 0.97s2.62 1.54 3.79 2.63z" fill="url(#fill_eyebrow_left)" filter="url(#blur_eyebrow_left)"/>
</g>
<g id="eye_right">
<path id="eyeball_right" d="m111.61 38.28c-2.39 1.65-4.4 3.94-5.38 6.68-1.24 3.45-0.77 7.31 0.43 10.77 1.22 3.55 3.27 6.93 6.36 9.06 1.54 1.07 3.33 1.8 5.19 2.02 1.87 0.22 3.8-0.09 5.47-0.95 2.02-1.06 3.57-2.91 4.53-4.98 0.96-2.08 1.37-4.37 1.5-6.66 0.16-2.9-0.12-5.86-1.08-8.61-1.04-2.99-2.92-5.75-5.58-7.47-1.32-0.86-2.83-1.45-4.4-1.67s-3.19-0.05-4.67 0.52c-0.84 0.33-1.62 0.78-2.37 1.29z" fill="url(#fill_eyeball_right)"/>
<g id="pupil_right">
<path id="pupil_right_base" d="m117.14 45.52c-0.9 0.06-1.78 0.37-2.55 0.85-0.76 0.48-1.41 1.13-1.92 1.88-1.03 1.49-1.48 3.31-1.55 5.12-0.05 1.35 0.1 2.72 0.55 4s1.2 2.47 2.25 3.33c1.07 0.89 2.42 1.42 3.81 1.49 1.39 0.06 2.79-0.34 3.93-1.13 0.91-0.63 1.64-1.5 2.16-2.48 0.52-0.97 0.84-2.05 0.98-3.15 0.25-1.93-0.03-3.95-0.93-5.69-0.89-1.74-2.41-3.17-4.24-3.84-0.8-0.29-1.65-0.44-2.49-0.38z" fill="#020204"/>
<path id="pupil_right_glare" d="m122.71 53.36c1-1-0.71-3.65-2.05-4.74-0.97-0.78-3.78-1.61-3.66-0.75 0.12 0.85 1.39 1.95 2.23 2.79 1.05 1.03 3 3.18 3.48 2.7z" clip-path="url(#clip_pupil_right)" fill="url(#fill_pupil_right_glare)" filter="url(#blur_pupil_right_glare)"/>
</g>
<path id="eyelid_right" d="m102.56 47.01c2.06-1.71 4.45-3.01 7-3.8 5.25-1.62 11.2-0.98 15.84 1.97 1.6 1.01 3.03 2.27 4.52 3.45 1.48 1.17 3.06 2.27 4.85 2.9 0.97 0.34 2 0.54 3.02 0.43 0.92-0.09 1.81-0.44 2.57-0.96 0.76-0.53 1.4-1.23 1.88-2.02 0.96-1.58 1.27-3.5 1.1-5.34-0.33-3.69-2.41-6.94-4.15-10.21-0.55-1.02-1.07-2.06-1.73-3.01-2.01-2.93-5.23-4.86-8.6-5.99s-6.93-1.54-10.46-1.98c-1.58-0.2-3.17-0.41-4.74-0.22-1.81 0.22-3.51 0.95-5.28 1.4-0.84 0.22-1.69 0.37-2.52 0.61s-1.65 0.57-2.33 1.11c-0.98 0.79-1.6 1.98-1.87 3.21-0.27 1.24-0.21 2.52-0.01 3.77 0.39 2.5 1.33 4.93 1.24 7.46-0.06 1.73-0.61 3.44-0.54 5.17 0.02 0.51 0.12 1.55 0.21 2.05z" clip-path="url(#clip_eye_right)" fill="url(#fill_eyelid_right)"/>
<path id="eyebrow_right" d="m119.93 31.18c-0.41 0.52-0.78 1.08-1.07 1.7 1.85 0.4 3.61 1.16 5.19 2.21 3.06 2.03 5.38 4.99 7.01 8.29 0.38-0.42 0.72-0.87 1.02-1.37-1.64-3.44-4-6.55-7.16-8.65-1.52-1-3.21-1.77-4.99-2.18z" fill="url(#fill_eyebrow_right)" filter="url(#blur_eyebrow_right)"/>
</g>
</g>
<g id="beak">
<g id="beak_shadow">
<path id="beak_shadow_lower" d="m81.12 89.33c1.47 4.26 4.42 7.89 7.92 10.72 1.16 0.95 2.39 1.82 3.76 2.43 1.36 0.62 2.87 0.97 4.36 0.84 1.46-0.12 2.85-0.7 4.13-1.42s2.46-1.59 3.7-2.37c2.12-1.35 4.39-2.44 6.6-3.64 2.65-1.45 5.23-3.1 7.46-5.14 1.03-0.93 1.98-1.95 3.11-2.75 1.13-0.81 2.49-1.39 3.87-1.29 1.04 0.07 2.01 0.51 3.03 0.73 0.51 0.11 1.03 0.16 1.55 0.08 0.51-0.08 1.01-0.29 1.37-0.67 0.44-0.46 0.64-1.12 0.61-1.76-0.02-0.63-0.24-1.25-0.54-1.81-0.59-1.13-1.49-2.1-1.89-3.31-0.36-1.08-0.29-2.24-0.26-3.37 0.03-1.14 0.01-2.32-0.51-3.33-0.4-0.76-1.07-1.37-1.83-1.77-0.76-0.41-1.62-0.62-2.48-0.7-1.72-0.16-3.44 0.18-5.17 0.27-2.28 0.13-4.58-0.15-6.87-0.02-2.85 0.18-5.65 1-8.51 1.01-3.26 0.01-6.52-1.06-9.74-0.55-1.39 0.22-2.71 0.72-4.03 1.16-1.33 0.45-2.7 0.84-4.1 0.82-1.59-0.03-3.13-0.58-4.72-0.69-0.79-0.06-1.6 0-2.35 0.28-0.74 0.28-1.41 0.79-1.78 1.5-0.21 0.4-0.31 0.86-0.33 1.31-0.02 0.46 0.04 0.91 0.15 1.36 0.22 0.88 0.63 1.71 0.96 2.55 1.2 3.07 1.46 6.42 2.53 9.53z" clip-path="url(#clip_body)" fill-opacity=".25882" filter="url(#blur_beak_shadow_lower)"/>
<path id="beak_shadow_upper" d="m77.03 77.2c2.85 1.76 5.41 3.93 7.56 6.39 1.99 2.29 3.68 4.89 6.29 6.58 1.83 1.2 4.04 1.87 6.28 2.08 2.63 0.24 5.29-0.15 7.83-0.84 2.35-0.63 4.62-1.53 6.7-2.71 3.97-2.25 7.28-5.55 11.65-7.03 0.95-0.33 1.94-0.56 2.86-0.96 0.92-0.39 1.79-0.99 2.23-1.83 0.42-0.82 0.4-1.75 0.54-2.64 0.15-0.96 0.48-1.88 0.66-2.83s0.2-1.96-0.24-2.83c-0.37-0.72-1.04-1.29-1.81-1.66-0.77-0.36-1.64-0.52-2.51-0.56-1.72-0.08-3.43 0.33-5.16 0.47-2.28 0.19-4.58-0.08-6.87-0.01-2.85 0.08-5.66 0.67-8.51 0.8-3.25 0.14-6.49-0.34-9.74-0.44-1.41-0.05-2.83-0.03-4.21 0.2-1.39 0.22-2.75 0.65-3.92 1.37-1.14 0.69-2.07 1.64-3.11 2.45-0.52 0.41-1.08 0.78-1.68 1.07-0.61 0.28-1.28 0.48-1.96 0.51-0.35 0.01-0.71-0.01-1.05 0.04-0.59 0.08-1.13 0.39-1.47 0.83-0.34 0.45-0.47 1.02-0.36 1.55z" clip-path="url(#clip_body)" filter="url(#blur_beak_shadow_upper)" opacity=".3"/>
</g>
<path id="beak_base" d="m91.66 58.53c1.53-1.71 2.57-3.8 4.03-5.56 0.73-0.88 1.58-1.69 2.57-2.26s2.15-0.89 3.29-0.79c1.27 0.11 2.46 0.74 3.39 1.61s1.62 1.97 2.17 3.12c0.53 1.11 0.95 2.28 1.71 3.24 0.81 1.02 1.94 1.71 2.97 2.52 0.51 0.4 1.01 0.83 1.41 1.34 0.41 0.51 0.72 1.1 0.86 1.74 0.13 0.65 0.06 1.33-0.16 1.95-0.23 0.62-0.61 1.18-1.09 1.64-0.95 0.92-2.25 1.42-3.56 1.6-2.62 0.37-5.27-0.41-7.92-0.34-2.67 0.08-5.29 1.02-7.97 0.93-1.33-0.05-2.69-0.38-3.79-1.14-0.55-0.39-1.03-0.88-1.38-1.45-0.34-0.57-0.55-1.23-0.58-1.9-0.02-0.64 0.13-1.28 0.39-1.86 0.25-0.59 0.61-1.12 1.01-1.62 0.81-0.99 1.8-1.81 2.65-2.77z" fill="url(#fill_beak_base)"/>
<g id="mandible_lower">
<path id="mandible_lower_base" d="m77.14 75.05c0.06 0.26 0.15 0.5 0.28 0.73 0.23 0.38 0.57 0.69 0.93 0.95 0.36 0.27 0.75 0.49 1.13 0.72 2.01 1.27 3.65 3.04 5.11 4.92 1.95 2.52 3.68 5.31 6.29 7.14 1.84 1.3 4.04 2.03 6.28 2.26 2.63 0.26 5.29-0.16 7.83-0.91 2.35-0.69 4.62-1.66 6.7-2.95 3.97-2.44 7.28-6.02 11.65-7.63 0.95-0.35 1.94-0.6 2.86-1.03 0.92-0.44 1.79-1.08 2.23-2 0.42-0.88 0.4-1.9 0.54-2.87 0.15-1.03 0.48-2.03 0.66-3.06s0.2-2.13-0.24-3.08c-0.37-0.78-1.04-1.4-1.81-1.79-0.77-0.4-1.64-0.58-2.51-0.62-1.72-0.08-3.43 0.36-5.16 0.52-2.28 0.21-4.58-0.09-6.87-0.02-2.85 0.09-5.66 0.73-8.51 0.87-3.25 0.15-6.49-0.35-9.74-0.48-1.41-0.06-2.83-0.04-4.22 0.2-1.39 0.23-2.75 0.71-3.91 1.51-1.13 0.78-2.03 1.84-3.07 2.74-0.52 0.45-1.08 0.86-1.7 1.16-0.61 0.3-1.29 0.49-1.98 0.47-0.35-0.01-0.72-0.06-1.05 0.04-0.21 0.07-0.4 0.2-0.56 0.35-0.16 0.16-0.29 0.34-0.41 0.52-0.29 0.42-0.54 0.87-0.75 1.34z" fill="url(#fill_mandible_lower_base)"/>
<path id="mandible_lower_glare" d="m89.9 78.56c-0.33 1.37-0.13 2.87 0.56 4.11 0.68 1.24 1.84 2.2 3.19 2.65 1.7 0.57 3.62 0.29 5.21-0.54 0.93-0.48 1.77-1.16 2.3-2.06 0.27-0.44 0.46-0.94 0.53-1.46 0.06-0.51 0.02-1.05-0.16-1.54-0.2-0.53-0.56-1-0.99-1.37-0.44-0.37-0.95-0.64-1.5-0.82-1.08-0.36-2.77-0.66-3.91-0.68-2.02-0.04-4.9 0.34-5.23 1.71z" clip-path="url(#clip_mandible_lower)" fill="#d9b30d" filter="url(#blur_mandible_lower_glare)"/>
</g>
<g id="mandible_upper">
<path id="mandible_upper_shadow" d="m84.31 67.86c-1.16 0.68-2.27 1.43-3.36 2.2-0.57 0.41-1.15 0.84-1.45 1.47-0.21 0.44-0.26 0.94-0.27 1.43 0 0.5 0.03 0.99-0.04 1.48-0.04 0.33-0.13 0.66-0.14 0.99-0.01 0.17 0 0.34 0.04 0.5 0.05 0.16 0.13 0.32 0.24 0.44 0.15 0.16 0.35 0.26 0.56 0.32s0.42 0.09 0.64 0.14c1.01 0.24 1.89 0.86 2.66 1.56 0.77 0.69 1.47 1.48 2.28 2.13 2.18 1.78 5.07 2.52 7.89 2.56 2.82 0.05 5.61-0.54 8.36-1.16 2.16-0.49 4.32-0.99 6.39-1.76 3.2-1.18 6.16-2.96 8.72-5.19 1.17-1.01 2.26-2.12 3.57-2.94 1.15-0.73 2.44-1.21 3.62-1.9 0.11-0.06 0.21-0.13 0.3-0.2 0.1-0.08 0.18-0.18 0.24-0.28 0.09-0.19 0.09-0.42 0.03-0.62s-0.18-0.38-0.31-0.55c-0.15-0.18-0.31-0.34-0.49-0.5-1.23-1.05-2.89-1.43-4.51-1.56-1.61-0.12-3.24-0.03-4.83-0.3-1.5-0.25-2.92-0.81-4.37-1.27-1.52-0.49-3.07-0.87-4.64-1.13-3.71-0.61-7.52-0.49-11.19 0.27-3.49 0.73-6.87 2.05-9.94 3.87z" clip-path="url(#clip_mandible_lower)" fill="#604405" filter="url(#blur_mandible_upper_shadow)"/>
<path id="mandible_upper_base" d="m83.94 63.95c-1.66 1.12-3.16 2.49-4.43 4.04-0.72 0.89-1.38 1.86-1.74 2.94-0.29 0.86-0.39 1.76-0.57 2.65-0.07 0.33-0.15 0.66-0.14 1 0 0.16 0.02 0.33 0.07 0.5 0.05 0.16 0.14 0.31 0.25 0.43 0.2 0.2 0.47 0.31 0.74 0.37 0.28 0.05 0.56 0.06 0.84 0.09 1.25 0.15 2.4 0.75 3.44 1.47 1.04 0.71 2 1.55 3.07 2.22 2.35 1.49 5.16 2.15 7.95 2.26 2.78 0.11 5.56-0.31 8.3-0.86 2.17-0.43 4.33-0.95 6.39-1.76 3.16-1.25 6.01-3.16 8.72-5.19 1.24-0.92 2.46-1.87 3.57-2.94 0.37-0.37 0.74-0.74 1.14-1.08 0.4-0.33 0.85-0.62 1.35-0.78 0.76-0.24 1.58-0.17 2.37-0.04 0.59 0.1 1.18 0.23 1.78 0.21 0.3-0.02 0.6-0.07 0.88-0.18s0.54-0.28 0.73-0.52c0.25-0.3 0.38-0.7 0.38-1.09 0-0.4-0.12-0.79-0.32-1.13-0.4-0.68-1.09-1.14-1.81-1.46-0.99-0.44-2.06-0.65-3.11-0.91-3.23-0.78-6.37-1.93-9.34-3.41-1.48-0.73-2.92-1.54-4.37-2.32-1.5-0.8-3.02-1.57-4.64-2.07-3.64-1.1-7.6-0.74-11.19 0.51-3.98 1.38-7.58 3.84-10.31 7.05z" fill="url(#fill_mandible_upper_base)"/>
<path id="mandible_upper_glare" d="m109.45 64.75c-0.2-0.24-0.48-0.42-0.78-0.51s-0.62-0.09-0.93-0.04c-0.62 0.11-1.18 0.44-1.7 0.8-1.47 1.01-2.77 2.26-3.91 3.64-1.5 1.83-2.74 3.94-3.16 6.27-0.07 0.39-0.11 0.8-0.07 1.19 0.05 0.4 0.2 0.79 0.49 1.07 0.24 0.25 0.58 0.4 0.92 0.45 0.35 0.05 0.71 0 1.04-0.11 0.66-0.22 1.21-0.69 1.74-1.15 2.87-2.58 5.47-5.66 6.51-9.38 0.1-0.37 0.19-0.75 0.19-1.14s-0.1-0.78-0.34-1.09z" clip-path="url(#clip_mandible_upper)" fill="#f6da4a" filter="url(#blur_mandible_upper_glare)"/>
<path id="naris_left" d="m92.72 59.06c-0.77-0.25-2.03 1.1-1.62 1.79 0.11 0.19 0.46 0.43 0.7 0.3 0.35-0.19 0.64-0.89 1.02-1.16 0.25-0.18 0.2-0.84-0.1-0.93z" fill="url(#fill_naris_left)" filter="url(#blur_naris_left)" opacity=".8"/>
<path id="naris_right" d="m102.56 59.42c0.2 0.64 1.23 0.53 1.83 0.84 0.52 0.27 0.94 0.86 1.53 0.88 0.56 0.01 1.44-0.2 1.51-0.76 0.09-0.73-0.98-1.2-1.67-1.47-0.89-0.34-2.03-0.52-2.86-0.06-0.19 0.11-0.4 0.36-0.34 0.57z" fill="url(#fill_naris_right)" filter="url(#blur_naris_right)" opacity=".8"/>
</g>
<path id="beak_corner" d="m129.27 69.15a2.42 3.1 16.94 0 1-2.81 3.04 2.42 3.1 16.94 0 1-2.12-3.04 2.42 3.1 16.94 0 1 2.81-3.05 2.42 3.1 16.94 0 1 2.12 3.05z" clip-path="url(#clip_beak)" fill="url(#fill_beak_corner)" filter="url(#blur_beak_corner)"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 45 KiB

3
docs/guides/manjaro.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" stroke="#35bf5c" stroke-width="4" viewBox="0 0 14 14">
<path d="M9 2H2V14 2M7 5v9M12 0V14"/>
</svg>

After

Width:  |  Height:  |  Size: 140 B

73
docs/guides/package.rst Normal file
View File

@ -0,0 +1,73 @@
Packaging Cahute
================
Cahute is constituted of a static library, a set of utilities, and a
set of udev rules. Due to the last element having to be installed for both,
only the following package organisations are available:
* Having one package for both the static library and utilities.
* Having one package for the static library, either a package for all utilities
or a package for each utility, and a package for installing the udev rules,
of which all previous package depend.
For more about the rationale behind this document, consult the
`Archlinux CMake packaging guidelines`_.
Preparing the dependencies
--------------------------
Cahute depends on the following, build-only dependencies:
* cmake_ >= 3.16;
* `GNU Make`_, `pkg-config`_, and other C compilation and linking utilities.
It also depends on the following build and runtime dependencies:
* libusb_;
* SDL_ >= 2.0 (for ``p7screen``).
Producing the distribution directory
------------------------------------
You first need to retrieve the source directory, named "cahute-|version|",
using one of the following methods:
* You can download the latest source package at
https://ftp.cahuteproject.org/releases\ :
.. parsed-literal::
curl -o cahute-|version|.tar.gz https\://ftp.cahuteproject.org/releases/cahute-|version|.tar.gz
tar xvaf cahute-|version|.tar.gz
* You can clone the repository and checkout the tag corresponding to the
release:
.. parsed-literal::
git clone https\://gitlab.com/cahuteproject/cahute.git cahute-|version|
(cd cahute-|version| && git checkout -f |version|)
Now that you have the source directory, you can build it using the following
commands:
.. parsed-literal::
cmake -B build -S cahute-|version| -DCMAKE_INSTALL_PREFIX=/usr
cmake --build build
Once this is done, you can produce the distribution directory ``dist/``
using the following command::
DESTDIR=./dist cmake --install build
Your distribution directory is ready; from there, the instructions are
specific to your distribution. Good luck!
.. _Archlinux CMake packaging guidelines:
https://wiki.archlinux.org/title/CMake_package_guidelines
.. _cmake: https://cmake.org/
.. _GNU Make: https://www.gnu.org/software/make/
.. _pkg-config: https://git.sr.ht/~kaniini/pkgconf
.. _SDL: https://www.libsdl.org/
.. _libusb: https://libusb.info/

64
docs/guides/report.rst Normal file
View File

@ -0,0 +1,64 @@
.. _issue-reporting-guide:
Reporting a vulnerability or issue
==================================
If you have identified that Cahute or any of its utilities does not work
properly, or would like to suggest a feature or evolution, there are
different procedures depending on the issue.
.. warning::
Once your issue is up or sent, **please check on it every few days at
least, or leave a way for the maintainers to contact you without
giving up their privacy** (i.e. no phone numbers, social network
profile or instant messaging address); an e-mail address is fine.
An issue reported by someone who can't answer once additional details
are required from them is an issue that gets closed and has wasted
everyone's time and efforts.
.. warning::
For any type of issue, due to the fact that Cahute is free software
maintained by people on their free time, there is no guarantee of any
delay, or even of a response or that the issue won't be closed due to
lack of availability on the maintainers' part.
Note however that this warning is worst case scenario, and hopefully,
it won't come to that for any correctly reported issue.
Reporting a vulnerability
-------------------------
If your issue has security implications, e.g. if it allows a malicious
device to access the host and/or execute arbitrary code without authorisation,
please send an e-mail to Thomas Touhey, the maintainer of Cahute,
at <security@cahuteproject.org>.
.. note::
Please only use this e-mail address if there is security implications
to your demand. If you are not sure if your issue qualifies or not,
send it anyway; use your best judgment.
.. _report-other-issues:
Reporting any other issue or evolution request
----------------------------------------------
For all other issues, you can create an issue on the `issue tracker at
Gitlab`_.
If you are effectively reporting a bug in your issue, please include the
following elements:
* System and version (e.g. Debian 14, Ubuntu 22.04 LTS, Windows 11 Pro, ...).
* Architecture (x64, ARM, ...).
* Cahute version (e.g. 0.1, 1.2.1, ...).
* If the issue regards a communication protocol: the calculator model and
OS version, e.g. Graph 90+E with OS 03.60.2202.
* **Steps to reproduce the issue**, on both the host and any device that
was implicated in the operation. (The order is important!)
.. _Issue tracker at Gitlab: https://gitlab.com/cahuteproject/cahute/-/issues

9
docs/headers.rst Normal file
View File

@ -0,0 +1,9 @@
Header reference
================
This section presents the code reference, by header.
.. toctree::
:maxdepth: 1
headers/cahute

13
docs/headers/cahute.rst Normal file
View File

@ -0,0 +1,13 @@
``<cahute.h>`` -- Main header for Cahute
========================================
.. toctree::
:maxdepth: 1
cahute/cdefs
cahute/detection
cahute/error
cahute/link
cahute/logging
cahute/osversion
cahute/picture

View File

@ -0,0 +1,289 @@
``<cahute/cdefs.h>`` -- Basic definitions for Cahute
====================================================
This header declares basic definitions for Cahute.
Macro definitions
-----------------
.. c:macro:: CAHUTE_PREREQ(MAJOR, MINOR)
Macro that returns whether the current version of Cahute is compatible
with the provided version of Cahute.
For example, ``CAHUTE_PREREQ(2, 4)`` checks if the current version of
Cahute is compatible with version 2.4.
.. c:macro:: OF(...)
Macro for defining parameter definitions in a function declaration.
For example::
int my_function OF((int arg1, char const *arg2));
This is present because in K&R C (pre-C89), function declarations did
not include parameters, i.e. the function declaration above would need
to render as the following::
int my_function();
.. c:macro:: CAHUTE_EXTERN(TYPE)
Macro to use in Cahute function declarations, surrounding the return type,
for compatibility. For example::
CAHUTE_EXTERN(int) my_function(int arg1, char const *arg2);
This is used to add calling conventions or other platform-specific options
in some cases, such as, on 16-bit x86 where the calling convention was
best described explicitely.
Example outputs of the above are the following::
int my_function(int arg1, char const *arg2); /* Default. */
_stdcall int my_function(int arg1, char const *arg2); /* WINAPI x86 */
int __cdecl my_function(int arg1, char const *arg2); /* GCC x86 */
extern __declspec(dllexport) int my_function(int arg1, char const *arg2); /* Borland C */
.. c:macro:: CAHUTE_LOCAL(TYPE)
Macro to use in Cahute function definitions, surrounding the return type,
as opposed to :c:macro:`CAHUTE_EXTERN`. For example::
CAHUTE_LOCAL(int) my_local_utility(int arg1, char const *arg2);
For now, this only produces the following output::
static int my_local_utility(int arg1, char const *arg2);
.. c:macro:: CAHUTE_INLINE(TYPE)
Macro to use in inlinable local Cahute function definitions, surrounding
the return type. This macro extends on :c:macro:`CAHUTE_LOCAL`'s meaning,
by making the function inlinable if the compiler is so inclined.
For example::
CAHUTE_INLINE(int) my_tiny_utility(int arg1, char const *arg2);
This can then use compiler-specific functions, such as GCC's
``always_inline`` attribute; see `GCC function attributes`_ for
more information.
.. c:macro:: CAHUTE_LOCAL_DATA(TYPE)
Macro to use in local data in Cahute source files, surrounding the
variable type, for example::
CAHUTE_LOCAL_DATA(char const *) my_string = "hello, world";
For now, this only produces the following output::
static char const * my_string = "hello, world";
.. c:macro:: CAHUTE_DEPRECATED
Function attribute, placed before the return type and
:c:macro:`CAHUTE_EXTERN`, that indicates that the compiler should
warn that the function is deprecated when compiling a user program or
library.
For example::
CAHUTE_DEPRECATED int my_deprecated_function(int a, int b);
.. c:macro:: CAHUTE_WUR
Function attribute, placed before the return type and
:c:macro:`CAHUTE_EXTERN`, that indicates that the compiler should
**w**\ arn in case of **u**\ nused **r**\ esult, i.e. if the caller
does not store nor process the resulting value from the function.
For example::
CAHUTE_WUR resource *create_resource(int a, int b);
.. c:macro:: CAHUTE_NNPTR(NAME)
Function parameter attribute that indicate that the passed value should
not be ``NULL``. It must be used in both the function declaration
and definition. For example::
int my_function(char const CAHUTE_NNPTR(my_string))
Calling this with an explicitely NULL pointer will raise a compiler
warning or error.
This may resolve as::
int my_function(char const *my_string); /* Default. */
int my_function(char const my_string[static 1]); /* C99. */
For maximum compatibility, this macro must be used with
:c:macro:`CAHUTE_NONNULL`.
.. c:macro:: CAHUTE_NONNULL(INDEXES)
Indicate, as an attribute, that one or more of the function arguments
should not be passed as NULL. For example::
int my_function(int *a, int *b, int *c) CAHUTE_NONNULL((1, 3));
Calling ``my_function`` with a NULL pointer for ``a`` or ``c`` will raise
a compiler warning or error.
This may resolve as::
int my_function(int *a, int *b, int *c); /* Default. */
int my_function(int *a, int *b, int *c) __attribute__((nonnull (1, 3))); /* Pre-C99 GCC. */
For maximum compatibility, this macro must be used with
:c:macro:`CAHUTE_NNPTR`.
.. c:macro:: CAHUTE_PRIu8
printf specifier for displaying :c:type:`cahute_u8` in decimal form,
e.g. ``hhu``.
.. c:macro:: CAHUTE_PRIu16
printf specifier for displaying :c:type:`cahute_u16` in decimal form,
e.g. ``hu``.
.. c:macro:: CAHUTE_PRIu32
printf specifier for displaying :c:type:`cahute_u32` in decimal form,
e.g. ``u``.
.. c:macro:: CAHUTE_PRIuSIZE
printf specifier for displaying ``size_t`` in decimal form, e.g. ``zu``.
.. c:macro:: CAHUTE_PRIx8
printf specifier for displaying :c:type:`cahute_u8` in lowercase
hexadecimal form, e.g. ``hhx``.
.. c:macro:: CAHUTE_PRIx16
printf specifier for displaying :c:type:`cahute_u16` in lowercase
hexadecimal form, e.g. ``hx``.
.. c:macro:: CAHUTE_PRIx32
printf specifier for displaying :c:type:`cahute_u32` in lowercase
hexadecimal form, e.g. ``x``.
.. c:macro:: CAHUTE_PRIxSIZE
printf specifier for displaying ``size_t`` in lowercase hexadecimal form,
e.g. ``zx``.
.. c:macro:: CAHUTE_PRIX8
printf specifier for displaying :c:type:`cahute_u8` in uppercase
hexadecimal form, e.g. ``hhX``.
.. c:macro:: CAHUTE_PRIX16
printf specifier for displaying :c:type:`cahute_u16` in uppercase
hexadecimal form, e.g. ``hX``.
.. c:macro:: CAHUTE_PRIX32
printf specifier for displaying :c:type:`cahute_u32` in uppercase
hexadecimal form, e.g. ``X``.
.. c:macro:: CAHUTE_PRIXSIZE
printf specifier for displaying ``size_t`` in uppercase hexadecimal form,
e.g. ``zX``.
Type definitions
----------------
.. c:type:: cahute_u8
Unsigned 8-bit integer type.
Available printf specifiers for this type are :c:macro:`CAHUTE_PRIu8`,
:c:macro:`CAHUTE_PRIx8` and :c:macro:`CAHUTE_PRIX8`.
.. c:type:: cahute_u16
Unsigned 16-bit integer type.
Available printf specifiers for this type are :c:macro:`CAHUTE_PRIu16`,
:c:macro:`CAHUTE_PRIx16` and :c:macro:`CAHUTE_PRIX16`.
.. c:type:: cahute_u32
Unsigned 32-bit integer type.
Available printf specifiers for this type are :c:macro:`CAHUTE_PRIu32`,
:c:macro:`CAHUTE_PRIx32` and :c:macro:`CAHUTE_PRIX32`.
Function declarations
---------------------
.. c:function:: cahute_u16 cahute_be16toh(cahute_u16 value)
Convert a 16-bit unsigned integer from big endian to host endianness.
:param value: 16-bit unsigned integer in big endian.
:return: 16-bit unsigned integer in host endianness.
.. c:function:: cahute_u16 cahute_le16toh(cahute_u16 value)
Convert a 16-bit unsigned integer from little endian to host endianness.
:param value: 16-bit unsigned integer in little endian.
:return: 16-bit unsigned integer in host endianness.
.. c:function:: cahute_u32 cahute_be32toh(cahute_u32 value)
Convert a 32-bit unsigned integer from big endian to host endianness.
:param value: 32-bit unsigned integer in big endian.
:return: 32-bit unsigned integer in host endianness.
.. c:function:: cahute_u32 cahute_le32toh(cahute_u32 value)
Convert a 32-bit unsigned integer from little endian to host endianness.
:param value: 32-bit unsigned integer in little endian.
:return: 32-bit unsigned integer in host endianness.
.. c:function:: cahute_u16 cahute_htobe16(cahute_u16 value)
Convert a 16-bit unsigned integer from host endianness to big endian.
:param value: 16-bit unsigned integer in host endianness.
:return: 16-bit unsigned integer in big endian.
.. c:function:: cahute_u16 cahute_htole16(cahute_u16 value)
Convert a 16-bit unsigned integer from host endianness to little endian.
:param value: 16-bit unsigned integer in host endianness.
:return: 16-bit unsigned integer in little endian.
.. c:function:: cahute_u32 cahute_htobe32(cahute_u32 value)
Convert a 32-bit unsigned integer from host endianness to big endian.
:param value: 32-bit unsigned integer in host endianness.
:return: 32-bit unsigned integer in big endian.
.. c:function:: cahute_u32 cahute_htole32(cahute_u32 value)
Convert a 32-bit unsigned integer from host endianness to little endian.
:param value: 32-bit unsigned integer in host endianness.
:return: 32-bit unsigned integer in little endian.
.. _GCC function attributes:
https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html

View File

@ -0,0 +1,114 @@
``<cahute/detection.h>`` -- Device detection for Cahute
=======================================================
This header declares device detection related utilities for Cahute.
Macro definitions
-----------------
``CAHUTE_USB_DETECTION_ENTRY_TYPE_*`` are constants representing the type of
device identified using the USB descriptor.
.. c:macro:: CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN
The device is an fx-9860G or compatible calculator, using Protocol 7.00
directly through bulk transfers.
Note that the fx-CG calculators sometimes use identify as an fx-9860G
for os updating or some types of screenstreaming.
.. c:macro:: CAHUTE_USB_DETECTION_ENTRY_TYPE_SCSI
The device is an fx-CG or compatible calculator, using SCSI with extensions
to communicate using Protocol 7.00 through vendor-specific commands.
Type definitions
----------------
.. c:struct:: cahute_serial_detection_entry
Available serial port that can be opened using
:c:func:`cahute_open_serial_link`.
.. c:member:: char const *cahute_serial_detection_entry_name
Null-terminated name or path of the serial port.
.. c:struct:: cahute_usb_detection_entry
Available USB device that can be opened using
:c:func:`cahute_open_usb_link`.
.. c:member:: int cahute_usb_detection_entry_bus
USB bus number of the entry.
.. c:member:: int cahute_usb_detection_entry_address
USB address number of the entry.
.. c:member:: int cahute_usb_detection_entry_type
Entry type, amongst the following:
.. c:macro:: CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN
The device is a Protocol 7.00 device over bulk.
See :ref:`protocol-seven` for more details.
.. c:macro:: CAHUTE_USB_DETECTION_ENTRY_TYPE_SCSI
The device is a UAS (USB-Attached SCSI) device with proprietary
extensions to communicate using Protocol 7.00.
See :ref:`protocol-ums` for more details.
See :ref:`usb-detection` for more information.
.. c:type:: int (*cahute_detect_serial_entry_func)(void *cookie, \
cahute_serial_detection_entry const *entry)
Function that can be called back with a serial detection entry.
See :c:func:`cahute_detect_serial` for more information.
.. c:type:: int (*cahute_detect_usb_entry_func)(void *cookie, \
cahute_usb_detection_entry const *entry)
Function that can be called back with a USB detection entry.
See :c:func:`cahute_detect_usb` for more information.
Function declarations
---------------------
.. c:function:: int cahute_detect_serial( \
cahute_detect_serial_entry_func *func, void *cookie)
Detect available serial devices.
For every found entry, the provided function is called with its cookie
and details regarding the serial entry, represented by its
``entry`` parameter of :c:type:`cahute_serial_detection_entry` type.
If the callback returns a non-zero value, it signals the current function
to stop and return the :c:macro:`CAHUTE_ERROR_INT` error.
:param func: Function to call with every entry.
:param cookie: Cookie to pass to the function.
:return: The error, or 0 if the operation was successful.
.. c:function:: int cahute_detect_usb(cahute_detect_usb_entry_func *func, \
void *cookie)
Detect available USB devices.
For every found entry, the provided function is called with its cookie
and details regarding the USB entry, represented by its
``entry`` parameter of :c:type:`cahute_usb_detection_entry` type.
If the callback returns a non-zero value, it signals the current function
to stop and return the :c:macro:`CAHUTE_ERROR_INT` error.
:param func: Function to call with every entry.
:param cookie: Cookie to pass to the function.
:return: The error, or 0 if the operation was successful.

View File

@ -0,0 +1,88 @@
``<cahute/error.h>`` -- Error definitions for Cahute
====================================================
This header declares error values and utilities for Cahute.
Macro definitions
-----------------
.. c:macro:: CAHUTE_OK
Error returned in case of success.
This is set to ``0``, so that in order to check if a Cahute function has
yielded an error, you can do the following:
.. code-block:: c
if (cahute_do_thing(1, 2)) {
fprintf(stderr, "An error has occurred.\n");
return EXIT_FAILURE;
}
.. c:macro:: CAHUTE_ERROR_UNKNOWN
Error raised if the cause of the error is unknown.
The logs can be investigated for more information.
.. c:macro:: CAHUTE_ERROR_IMPL
Error raised if a required feature or code path was unimplemented.
.. c:macro:: CAHUTE_ERROR_ALLOC
Error raised if a memory allocation has failed.
.. c:macro:: CAHUTE_ERROR_PRIV
Error raised if a system privilege error has been encountered.
.. c:macro:: CAHUTE_ERROR_INT
Error raised if, for a function taking a callback and calling it
with every iteration, said callback has returned ``1`` on a given
iteration, meaning the iteration was *INTerrupted*.
.. c:macro:: CAHUTE_ERROR_SIZE
Error raised if an incoming message was too big for the corresponding
internal buffers.
.. c:macro:: CAHUTE_ERROR_NOT_FOUND
Error code raised if a device could not be found using the provided
identification (name, path, or bus identification).
.. c:macro:: CAHUTE_ERROR_TOO_MANY
Error raised if only a single device was expected, but multiple were
found.
.. c:macro:: CAHUTE_ERROR_INCOMPAT
Error raised if a device was not suitable to be opened to be used by
a link.
.. c:macro:: CAHUTE_ERROR_GONE
Error raised if a device with which communication was previously
established is no longer accessible.
.. c:macro:: CAHUTE_ERROR_TIMEOUT
Error raised if a timeout has been encountered on an I/O operation.
.. c:macro:: CAHUTE_ERROR_CORRUPT
Error raised if an incoming packet had invalid format, or an invalid
checksum.
.. c:macro:: CAHUTE_ERROR_IRRECOV
Error raised if the link was previously deemed irrecoverable, and as such,
the current operation could not be executed.
.. c:macro:: CAHUTE_ERROR_NOOW
Error raised if overwrite was requested and rejected by either us or
the calculator.

View File

@ -0,0 +1,614 @@
``<cahute/link.h>`` -- Calculator link resource and methods for Cahute
======================================================================
This header declares link-related utilities for Cahute.
Type definitions
----------------
.. c:struct:: cahute_device_info
Device information.
.. c:member:: unsigned long cahute_device_info_flags
Flags for the link information.
.. c:macro:: CAHUTE_DEVICE_INFO_FLAG_PREPROG
Preprogrammed ROM information available.
.. c:macro:: CAHUTE_DEVICE_INFO_FLAG_BOOTCODE
Bootcode information available.
.. c:macro:: CAHUTE_DEVICE_INFO_FLAG_OS
OS information available.
.. c:member:: unsigned long cahute_device_info_rom_capacity
Preprogrammed ROM capacity, in KiB.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_PREPROG`
flag is set.
.. c:member:: cahute_os_version cahute_device_info_rom_version
Preprogrammed ROM version.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_PREPROG`
flag is set.
.. c:member:: unsigned long cahute_device_info_flash_rom_capacity
Flash ROM capacity, in KiB.
.. c:member:: unsigned long cahute_device_info_ram_capacity
RAM capacity, in KiB.
.. c:member:: cahute_version cahute_device_info_bootcode_version
Bootcode version.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_BOOTCODE`
flag is set.
.. c:member:: unsigned long cahute_device_info_bootcode_offset
Bootcode offset.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_BOOTCODE`
flag is set.
.. c:member:: unsigned long cahute_device_info_bootcode_size
Bootcode size, in KiB.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_BOOTCODE`
flag is set.
.. c:member:: cahute_version cahute_device_info_os_version
OS version.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_OS` flag
is set.
.. c:member:: unsigned long cahute_device_info_os_offset
OS offset.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_OS` flag
is set.
.. c:member:: unsigned long cahute_device_info_os_size
OS size, in KiB.
Only available if the :c:macro:`CAHUTE_DEVICE_INFO_FLAG_OS` flag
is set.
.. c:member:: char const *cahute_device_info_product_id
Null-terminated product identifier, up to 16 characters.
.. c:member:: char const *cahute_device_info_username
Null-terminated username, up to 20 characters long.
.. c:member:: char const *cahute_device_info_organisation
Null-terminated organisation, up to 20 characters long.
.. c:member:: char const *cahute_device_info_hwid
Null-terminated hardware identifier, up to 8 characters.
.. c:member:: char const *cahute_device_info_cpuid
Null-terminated hardware platform identifier, up to 16 characters.
.. c:struct:: cahute_frame
Screenstreaming frame details for screenstreaming.
.. c:member:: int cahute_frame_width
Width of the frame, in pixels.
.. c:member:: int cahute_frame_height
Height of the frame, in pixels.
.. c:member:: int cahute_frame_format
Format of the frame, as a ``CAHUTE_PICTURE_FORMAT_*`` constant.
See :ref:`header-cahute-picture` for more information.
.. c:member:: cahute_u8 const *cahute_frame_data
Frame contents encoded with the format described above.
.. c:struct:: cahute_storage_entry
Entry when listing the contents of a storage device or directory.
.. c:member:: char const *cahute_storage_entry_directory
If the entry is a directory, the name of the directory.
If the entry is a file, the optional name of the directory in
which the file is present; this can be set to ``NULL`` if the
file is present at root.
.. c:member:: char const *cahute_storage_entry_name
If the entry is a directory, this is set to ``NULL``.
If the entry is a file, the file name.
.. c:member:: unsigned long cahute_storage_entry_size
Size in bytes of the file.
.. c:struct:: cahute_link
Link to a calculator, that can be used to run operations on the
calculator, or receive data such as screenstreaming data.
This type is opaque, and such resources must be created using
:c:func:`cahute_open_usb_link` or :c:func:`cahute_open_serial_link`.
.. c:type:: int (cahute_process_frame_func)(void *cookie, \
cahute_frame const *frame)
Function that can be called when a frame has been received in a
screenstreaming mode.
See :c:func:`cahute_receive_screen` for more information.
.. c:type:: int (cahute_confirm_overwrite_func)(void *cookie)
Function that can be called to confirm overwrite.
See :c:func:`cahute_send_file_to_storage` for more information.
.. c:type:: int (cahute_list_storage_entry_func)(void *cookie, \
cahute_storage_list_entry const *entry)
Function that can be called for every storage device entry.
See :c:func:`cahute_list_storage_entries` for more information.
.. c:type:: int (cahute_progress_func)(void *cookie, unsigned long step,\
unsigned long total)
Function that can be called to display progress, when step ``step`` out
of ``total`` has just finished.
See :c:func:`cahute_send_file_to_storage` and
:c:func:`cahute_request_file_from_storage` for more information.
Function declarations
---------------------
.. c:function:: int cahute_open_serial_link(cahute_link **linkp, \
unsigned long flags, char const *name, unsigned long speed)
Open a link over a serial modem.
.. warning::
In case of error, the value of ``*linkp`` mustn't be used nor freed.
Since serial links do not offer any metadata, the protocol to use on the
serial link must be selected manually, amongst the following:
.. c:macro:: CAHUTE_SERIAL_PROTOCOL_SEVEN
Use Protocol 7.00.
See :ref:`protocol-seven` for more information.
Since the number of stop bits may be selectable on the calculator, it
can also be selected manually, amongst the following:
.. c:macro:: CAHUTE_SERIAL_STOP_ONE
Use 1 stop bit.
.. c:macro:: CAHUTE_SERIAL_STOP_TWO
Use 2 stop bits (*by default*).
Since the parity may also be selectable on the calculator, it can also
be selected manually, amongst the following:
.. c:macro:: CAHUTE_SERIAL_PARITY_OFF
Disable parity checks (*by default*).
.. c:macro:: CAHUTE_SERIAL_PARITY_EVEN
Use even parity checks.
.. c:macro:: CAHUTE_SERIAL_PARITY_ODD
Use odd parity checks.
.. note::
fx-9860G calculators and derivatives, i.e. the ones you will be the
most likely to encounter, use protocol 7.00 with 2 stop bits and
no parity when establishing a new connection.
The LINK app on such calculators does not allow to change these
settings, and the only way for the link to use different settings
is if the connection has already been established and command
:ref:`seven-command-02` was issued to change the serial link
parameters for the current connection.
If the device uses XON/XOFF software control, it can also be selected
manually, amongst the following:
.. c:macro:: CAHUTE_SERIAL_XONXOFF_DISABLE
Disable XON/XOFF software control (*by default*).
.. c:macro:: CAHUTE_SERIAL_XONXOFF_ENABLE
Enable XON/XOFF software control.
If the device uses DTR/RTS and the cable may support it, it can also be
selected manually, amongst the following:
.. c:macro:: CAHUTE_SERIAL_DTRRTS_DISABLE
Disable DTR/RTS (*by default*).
.. c:macro:: CAHUTE_SERIAL_DTRRTS_ENABLE
Enable DTR/RTS.
.. c:macro:: CAHUTE_SERIAL_DTRRTS_HANDSHAKE
Enable DTR/RTS, and require a DTR/RTS handshake to be done.
Protocol-specific behaviour can be tweaked using the following flags:
.. c:macro:: CAHUTE_SERIAL_NOCHECK
If this flag is provided, the initial handshake will not be
done when the link is established on the underlying medium.
This flag is mostly useful when resuming a connection initiated by
another process, or when the passive process does not require or
implement the initial handshake.
It is only effective when using protocol 7.00.
See :ref:`protocol-seven` for more information.
.. c:macro:: CAHUTE_SERIAL_NODISC
If this flag is provided, command :ref:`seven-command-01` is
not issued once the link is established to get the device information.
This flag is mostly useful when dealing with bootcode or custom
link implementations that may not have implemented this command.
It is not recommended when communicating with the LINK application
since it enables Cahute to predict which commands will be
unavailable without crashing the link.
It is only effective when using protocol 7.00.
See :ref:`protocol-seven` for more information.
.. c:macro:: CAHUTE_SERIAL_NOTERM
If this flag is provided, the terminate flow is not run when
the link is closed.
This flag is mostly useful to let the connection still opened for
other processes to run commands. Combined with
:c:macro:`CAHUTE_SERIAL_NOCHECK`, it allows running multiple shell
commands on the same connection.
It is only effective when using protocol 7.00.
See :ref:`protocol-seven` for more information.
.. c:macro:: CAHUTE_SERIAL_OHP
If this flag is provided, the calculator is assumed to use the link
for screenstreaming purposes.
For example, with the fx-9860G and compatible, this prompts the link
to use Protocol 7.00 Screenstreaming instead of Protocol 7.00.
See :ref:`protocol-seven-ohp` for more information.
:param linkp: The pointer to set the opened link to.
:param flags: The flags to set to the serial link.
:param name: The name or path of the serial link to open.
:param speed: The speed (in bauds) to open the serial link with, or ``0``
to select the default serial speed.
:return: The error, or 0 if the operation was successful.
.. c:function:: int cahute_open_usb_link(cahute_link **linkp, \
unsigned long flags, int bus, int address)
Open a link with a USB device.
.. warning::
In case of error, the value of ``*linkp`` mustn't be used nor freed.
The protocol to use is determined using the USB device metadata.
See :ref:`usb-detection` for more information.
Protocol-specific behaviour can be tweaked using the following flags:
.. c:macro:: CAHUTE_USB_NOCHECK
If this flag is provided, the initial handshake will not be
done when the link is established on the underlying medium.
This flag is mostly useful when resuming a connection initiated by
another process, or when the passive process does not require or
implement the initial handshake.
It is only effective when using protocol 7.00.
See :ref:`protocol-seven` for more information.
.. c:macro:: CAHUTE_USB_NODISC
If this flag is provided, command :ref:`seven-command-01` is
not issued once the link is established to get the device information.
This flag is mostly useful when dealing with bootcode or custom
link implementations that may not have implemented this command.
It is not recommended when communicating with the LINK application
since it enables Cahute to predict which commands will be
unavailable without crashing the link.
It is only effective when using protocol 7.00.
See :ref:`protocol-seven` for more information.
.. c:macro:: CAHUTE_USB_NOTERM
If this flag is provided, the terminate flow is not run when
the link is closed.
This flag is mostly useful to let the connection still opened for
other processes to run commands. Combined with
:c:macro:`CAHUTE_USB_NOCHECK`, it allows running multiple shell
commands on the same connection.
It is only effective when using protocol 7.00.
See :ref:`protocol-seven` for more information.
.. c:macro:: CAHUTE_USB_OHP
If this flag is provided, the calculator is assumed to use the link
for screenstreaming purposes.
For example, with the fx-9860G and compatible, this prompts the link
to use Protocol 7.00 Screenstreaming instead of Protocol 7.00.
See :ref:`protocol-seven-ohp` for more information.
:param linkp: The pointer to set the opened link to.
:param flags: The flags to set to the serial link.
:param bus: The bus number of the USB calculator to open a link with.
:param address: The device number of the calculator to open a link with.
:return: The error, or 0 if the operation was successful.
.. c:function:: void cahute_close_link(cahute_link *link)
Close and free a link.
:param link: The link to close.
.. c:function:: int cahute_get_device_info(cahute_link *link, \
cahute_device_info **infop)
Gather information on the device (calculator or other).
.. warning::
In all cases, ``*infop`` **musn't be freed**.
In case of error, ``*infop`` mustn't be used.
:param link: The link on which to gather information.
:param infop: The pointer to set to the information to.
:return: The error, or 0 if the operation was successful.
.. c:function:: int cahute_negotiate_serial_params(cahute_link *link, \
unsigned long flags, unsigned long speed)
Negotiate new parameters for a serial link, and update the medium
parameters to these.
The accepted flags here are among ``CAHUTE_SERIAL_STOP_*`` and
``CAHUTE_SERIAL_PARITY_*``.
:param link: Link to which to define the new attributes.
:param flags: New flags to set to the serial link.
:param speed: New speed to set to the serial link.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_receive_screen(cahute_link *link, \
cahute_process_frame_func *callback, void *cookie)
Receive screen continuously, using screenstreaming, and call the provided
function for every received frame.
If the provided function returns any non-zero value, the process is
interrupted.
:param link: Link with which to receive screen frames.
:param callback: Function to call for every received frame.
:param cookie: Cookie to provide to the function on every call.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_request_storage_capacity(cahute_link *link, \
char const *storage, unsigned long *capacityp)
Request the currently available capacity on the provided storage device.
If this function returns an error, the contents of ``*capacityp`` is
left intact, and may be undefined.
:param link: Link to the device.
:param storage: Name of the storage device for which to get the
currently available capacity.
:param capacityp: Pointer to the capacity to fill.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_send_file_to_storage(cahute_link *link, \
unsigned long flags, char const *directory, char const *name, \
char const *storage, FILE *filep, \
cahute_confirm_overwrite_func *overwrite_func, void *overwrite_cookie, \
cahute_progress_func *progress_func, void *progress_cookie)
Send a file to a storage device on the calculator.
.. note::
The provided ``filep`` parameter will be used for reading and
**seeking**, in order to estimate the file size.
If an overwrite confirmation function is provided and the calculator
requests confirmation for overwriting, said function is called. Then:
* If the function returns ``0``, the overwrite is rejected, therefore
the file is not sent.
* If the function returns any other value, the overwrite is confirmed,
and the function transfers the file over.
Flags that can be set to this function are the following:
.. c:macro:: CAHUTE_SEND_FILE_FLAG_FORCE
If this flag is set, overwrite is forced without using the
callback functions.
.. c:macro:: CAHUTE_SEND_FILE_FLAG_OPTIMIZE
If this flag is set, the available capacity in the targeted storage
is requested. If it is not considered enough to store the file, an
optimize command will be issued beforehand.
See :ref:`seven-send-file-to-storage` for the use case with Protocol 7.00.
:param link: Link to the device.
:param flags: Flags for the function.
:param directory: Name of the directory in which to place the file,
or ``NULL`` if the file should be placed at root.
:param name: Name of the file in the target storage device.
:param storage: Name of the storage device on which to place the file.
:param filep: Standard FILE pointer to read file data and estimate
file size from.
:param overwrite_func: Pointer to the overwrite function to call.
If this is set to ``NULL``, the overwrite will be systematically
rejected if requested by the calculator.
:param overwrite_cookie: Cookie to pass to the overwrite
confirmation function.
:param progress_func: Pointer to the optional progress function to call
once for every step in the transfer process.
:param progress_cookie: Cookie to pass to the progress function.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_request_file_from_storage(cahute_link *link, \
char const *directory, char const *name, char const *storage, \
FILE *filep, cahute_progress_func *progress_func, void *progress_cookie)
Request a file from a storage device on the calculator.
See :ref:`seven-request-file-from-storage` for the use case with
Protocol 7.00.
:param link: Link to the device.
:param directory: Name of the directory from which to request the file,
or ``NULL`` if the file should be placed at root.
:param name: Name of the file to request.
:param storage: Name of the storage device from which to request the file.
:param filep: Standard FILE pointer to write file data.
:param progress_func: Pointer to the optional progress function to call
once for every step in the transfer process.
:param progress_cookie: Cookie to pass to the progress function.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_copy_file_on_storage(cahute_link *link, \
char const *source_directory, char const *source_name, \
char const *target_directory, char const *target_name, char const *storage)
Copy a file on a storage device on the calculator.
See :ref:`seven-copy-file-on-storage` for the use case with
Protocol 7.00.
:param link: Link to the device.
:param source_directory: Name of the directory in which to retrieve
the source file.
:param source_name: Name of the source file to copy.
:param target_directory: Name of the directory in which to create
the copy.
:param target_name: Name of the copy.
:param storage: Name of the storage device on which to copy.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_delete_file_from_storage(cahute_link *link, \
char const *directory, char const *name, char const *storage)
Delete a file from a storage device on the calculator.
See :ref:`seven-delete-file-on-storage` for the use case with
Protocol 7.00.
:param link: Link to the device.
:param directory: Name of the directory from which to delete the file,
or ``NULL`` if the file should be placed at root.
:param name: Name of the file to delete.
:param storage: Name of the storage device from which to delete the file.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_list_storage_entries(cahute_link *link, \
char const *storage, cahute_list_storage_entry_func *callback, \
void *cookie)
List files and directories on a storage device on the calculator.
For every entry, the callback function is called. If it returns a value
other than ``0``, the file listing is interrupted.
See :ref:`seven-list-files-on-storage` for the use case with
Protocol 7.00.
:param link: Link to the device.
:param storage: Storage on which to list files and directories.
:param func: Function to call back with every found entry.
:param cookie: Cookie to pass to the function.
:return: Error, or 0 if the operation was unsuccessful.
.. c:function:: int cahute_reset_storage(cahute_link *link, \
char const *storage)
Request a reset of the provided storage device by the calculator.
See :ref:`seven-reset-storage` for the use case with Protocol 7.00.
:param link: Link to the device.
:param storage: Name of the storage device.
:return: Error, or 0 if the operation was successful.
.. c:function:: int cahute_optimize_storage(cahute_link *link, \
char const *storage)
Request optimization for the provided storage device by the calculator.
See :ref:`seven-optimize-storage` for the use case with Protocol 7.00.
:param link: Link to the device.
:param storage: Name of the storage device.
:return: Error, or 0 if the operation was successful.

View File

@ -0,0 +1,41 @@
``<cahute/logging.h>`` -- Logging control for Cahute
====================================================
Macro definitions
-----------------
.. c:macro:: CAHUTE_LOGLEVEL_INFO
Constant representing the ``info`` logging level; see :ref:`logging`
for more information.
.. c:macro:: CAHUTE_LOGLEVEL_WARNING
Constant representing the ``warning`` logging level; see :ref:`logging`
for more information.
.. c:macro:: CAHUTE_LOGLEVEL_ERROR
Constant representing the ``error`` logging level; see :ref:`logging`
for more information.
.. c:macro:: CAHUTE_LOGLEVEL_FATAL
Constant representing the ``fatal`` logging level; see :ref:`logging`
for more information.
.. c:macro:: CAHUTE_LOGLEVEL_NONE
Constant representing the special ``none`` logging level; see
:ref:`logging` for more information.
Function declarations
---------------------
.. c:function:: int cahute_get_log_level()
:return: The current logging level.
.. c:function:: void cahute_set_log_level(int level)
:param level: The logging level to set as the current one.

View File

@ -0,0 +1,108 @@
``<cahute/osversion.h>`` -- CASIO OS version format utilities for Cahute
========================================================================
Macro definitions
-----------------
``CAHUTE_VERSION_ZONE_*`` represent geographical zones
(markets) for which the software is produced.
.. c:macro:: CAHUTE_VERSION_ZONE_NONE
International.
.. c:macro:: CAHUTE_VERSION_ZONE_AUS
Australia.
.. c:macro:: CAHUTE_VERSION_ZONE_FR
France.
.. c:macro:: CAHUTE_VERSION_ZONE_NAM
North America.
.. c:macro:: CAHUTE_VERSION_ZONE_CH
China.
.. c:macro:: CAHUTE_VERSION_ZONE_SING
Singapour.
``CAHUTE_VERSION_MATH_*`` represent the available math
input/output features for the software.
.. c:macro:: CAHUTE_VERSION_MATH_SLIM
Slim.
.. c:macro:: CAHUTE_VERSION_MATH_ALL
All features (fx-9860GII-2).
.. c:macro:: CAHUTE_VERSION_MATH_REDUCED
Reduced features (fx-7400GII)
.. c:macro:: CAHUTE_VERSION_MATH_NONE
No features.
``CAHUTE_VERSION_STATUS_*`` represent the build type for
the software.
.. c:macro:: CAHUTE_VERSION_STATUS_STANDARD
Standard build.
.. c:macro:: CAHUTE_VERSION_STATUS_INDEV
Special / Development build.
``CAHUTE_VERSION_PLATFORM_*`` represent the hardware platform
on which the software runs.
.. c:macro:: CAHUTE_VERSION_PLATFORM_BASIC
SH7337 / SH7355.
.. c:macro:: CAHUTE_VERSION_PLATFORM_SPECIAL
SH7305.
.. c:macro:: CAHUTE_VERSION_PLATFORM_PRIZM
SH7305 (Prizm).
Type definitions
----------------
.. c:struct:: cahute_os_version
CASIO OS version structure, determined from a version string.
.. c:member:: int cahute_version_major
Major version.
.. c:member:: int cahute_version_minor
Minor version.
.. c:member:: cahute_version_zone cahute_version_zone
Geographical zone (market).
.. c:member:: cahute_version_math cahute_version_math
Available math I/O features.
.. c:member:: cahute_version_status cahute_version_status
Build type.
.. c:member:: cahute_version_platform cahute_version_platform
Hardware platform.

View File

@ -0,0 +1,26 @@
.. _header-cahute-picture:
``<cahute/picture.h>`` -- Picture format related utilities for Cahute
=====================================================================
Macro definitions
-----------------
``CAHUTE_PICTURE_FORMAT_*`` are constants representing how a given
picture's data is encoded.
.. c:macro:: CAHUTE_PICTURE_FORMAT_1BIT_MONO
Constant representing the :ref:`picture-format-1bit`.
.. c:macro:: CAHUTE_PICTURE_FORMAT_1BIT_DUAL
Constant representing the :ref:`picture-format-2bit-dual`.
.. c:macro:: CAHUTE_PICTURE_FORMAT_4BIT_RGB_PACKED
Constant representing the :ref:`picture-format-4bit-rgb-packed`.
.. c:macro:: CAHUTE_PICTURE_FORMAT_16BIT_R5G6B5
Constant representing the :ref:`picture-format-r5g6b5`.

38
docs/index.rst Normal file
View File

@ -0,0 +1,38 @@
Cahute |version|
================
.. warning::
Note that Cahute **is not released yet**, and such, tarballs have
not been built yet, and the documentation and library structures
is susceptible to change without warning.
**Please do not attempt to install Cahute until version 0.1 is released.**
Cahute is a library and set of command-line utilities to handle serial
and USB communication protocols and file formats related to CASIO calculators,
dating from the 1990s to today.
The project is also present `on Gitlab <Cahute on Gitlab_>`_.
It is maintained by `Thomas Touhey`_.
The project's code and documentation contents are licensed under CeCILL_
version 2.1 as distributed by the CEA, CNRS and Inria on
`cecill.info <CeCILL_>`_.
This documentation is organized using `Diátaxis`_' structure.
.. toctree::
:maxdepth: 3
acknowledgements
guides
topics
project
cli
headers
.. _Cahute on Gitlab: https://gitlab.com/cahuteproject/cahute
.. _Thomas Touhey: https://thomas.touhey.fr/
.. _CeCILL: http://www.cecill.info/licences.en.html
.. _Diátaxis: https://diataxis.fr/

12
docs/project.rst Normal file
View File

@ -0,0 +1,12 @@
Project management
==================
These documents describe the decisions taken for the project to handle
collaboration efficiently.
.. toctree::
project/governance
project/versioning
project/contribution-style
project/coding-style

View File

@ -0,0 +1,111 @@
.. _coding-style:
Coding style
============
Cahute is made to be compatible with as much platforms as possible, adopting
the lowest C standard (C89/C90) and protecting platform-specific usage
behind macros as much as possible.
Formatting
----------
In order to focus as much as possible on the work that matters, Cahute relies
heavily on autoformatting and uncompromising sets of rules enforceable through
tooling. This includes:
* For C code, the use the `clang-format`_ with a set of rules described in
``.clang-format``, installed as a `pre-commit`_ hook.
* For Python code, the use of Black_.
Otherwise, the rules are not yet clearly defined, and may be as a few
contributions have gone by, and the standard for Cahute shapes itself a
bit more clearly.
Namespace
---------
Cahute reserves both the ``cahute_`` and ``CAHUTE_`` prefixes for all
symbols and macros, i.e. it is recommended a library user does not use
that prefix in any of their work.
Cahute respects reserved namespaces from C and POSIX; see
`Open Group Base Specifications Issue 7, section 2.2.2 The Name Space`_
for more information.
.. note::
This notably prevents us from using the ``_t`` suffix, since it
is reserved by POSIX / Single UNIX Specification.
To avoid overlapping with user macros, **function and macro parameters are also
defined within the Cahute namespaces**, more specifically in the ``cahute__``
and ``CAHUTE__`` (*double underscore*) namespaces.
For example, a preprocessed Cahute macro and function declaration in public
headers are the following::
#define CAHUTE_MY_MACRO(CAHUTE__ARG1, CAHUTE__ARG2) ...
int cahute_open_usb_link(
cahute_link *cahute__linkp,
unsigned long cahute__flags,
int cahute__bus,
int cahute__address
);
.. note::
The namespace mainly applies to public headers from Cahute.
Within internal headers and source files, **only exported symbols
need to be placed within the reserved namespaces**.
Exported symbols in the Cahute static library can be found using ``nm``:
.. code-block:: bash
nm -CgU libcahute.a
Compatibility
-------------
Cahute is designed to be more compatible than not, but to make use of
compiler and platform specific tools for static analysis if possible.
As such, you must use the relevant compatibility macros for your case:
* Both function declarations and definitions must use the following:
- Either :c:macro:`CAHUTE_EXTERN`, or the function is only to be used by
other code **in the same file**, :c:macro:`CAHUTE_LOCAL` or
:c:macro:`CAHUTE_INLINE`.
Functions internal to Cahute but used cross-file must make use of
:c:macro:`CAHUTE_EXTERN`, but be declared in ``lib/internals.h`` instead
of the public headers.
- :c:macro:`CAHUTE_NNPTR` and :c:macro:`CAHUTE_NONNULL` together, where
relevant.
* Function declarations only must use the following:
- :c:macro:`OF`\ ``((int arg1, char const *arg2, ...))`` for parameters.
- :c:macro:`CAHUTE_DEPRECATED` and :c:macro:`CAHUTE_WUR` where relevant.
**Only utilities available in C89 / C90 must be used.**
For easier implementation, the following out-of-standard general utilities
are available:
* Portable fixed-width integer types, in the form of :c:type:`cahute_u8`,
:c:type:`cahute_u16` and :c:type:`cahute_u32`.
* Portable printf specifiers for :c:type:`cahute_u8`, :c:type:`cahute_u16`,
:c:type:`cahute_u32` and ``size_t``.
* Endianness conversion utilities for both 16-bit and 32-bit integers,
as :c:func:`cahute_be16toh`, :c:func:`cahute_le16toh`,
:c:func:`cahute_be32toh`, :c:func:`cahute_le32toh`, :c:func:`cahute_htobe16`,
:c:func:`cahute_htole16`, :c:func:`cahute_htobe32`, :c:func:`cahute_htole32`.
.. _pre-commit: https://pre-commit.com/
.. _clang-format: https://clang.llvm.org/docs/ClangFormat.html
.. _Black: https://github.com/psf/black
.. _`Open Group Base Specifications Issue 7, section 2.2.2 The Name Space`:
https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
#tag_15_02_02

View File

@ -0,0 +1,62 @@
.. _contribution-style:
Contribution style
==================
TL;DR:
* Contributors, especially external contributors, must work on a fork, and
submit their work via a merge request.
* Contributors can only target ``develop``, except maintainers that can
target other release branches to backport fixes if need be.
* Contributors must use `pre-commit`_ for autoformatting and linting their
contribution.
* Contributors must write documentation for their modifications on the
library.
.. _project-commit-naming:
Commit naming
-------------
Cahute uses `Conventional Commits`_, as enforced using Commitizen_ installed
as a `pre-commit`_ hook.
Your commit messages must be in **imperative** form, i.e. ``do something``.
.. warning::
Please do **NOT** use past form, e.g. ``done something``.
.. note::
Scopes in commit messages aren't common in Cahute, but they can be
accepted if they are relevant.
Coding style
------------
The code included within the contribution must respect Cahute's coding
style; see :ref:`coding-style` for more information.
Documentation
-------------
Every modification on library code must be repercuted on the documentation.
Think of it that way:
* If it is wrong in the code, it is most certainly wrong in the documentation.
* If it is not present in the documentation, then it is missing, and must be
added into it.
* If it is added to the code, it must be added to the documentation.
Writing documentation is hard, and Diátaxis_' logic has to be learned in
order to be applied efficiently. However, even bad documentation is
documentation, and Cahute needs it to be sustainable, especially since
Cahute implements reverse engineered protocols and file formats for which
no official documentation exists!
.. _pre-commit: https://pre-commit.com/
.. _Commitizen: https://commitizen-tools.github.io/commitizen/
.. _Conventional Commits: https://www.conventionalcommits.org/en/v1.0.0/
.. _Diátaxis: https://diataxis.fr/

View File

@ -0,0 +1,19 @@
Project governance
==================
`Thomas Touhey <https://thomas.touhey.fr/>`_ is the `Benevolent Dictator For
Life`_ (BDFL) and only maintainer on this project, until a more sustainable
solution is found and put into place or the project is made obsolete.
Contributions may be accepted by maintainers as long as they respect
the :ref:`contribution-style`.
Issues may be addressed by maintainers or external contributors as long
as they follow the issue reporting guide; see :ref:`issue-reporting-guide`.
Changes on the project management can only be made or accepted by the BDFL,
unless a more sustainable model is found and put into place. Contributions
making such changes can only be accepted by the BDFL.
.. _Benevolent Dictator For Life:
https://en.wikipedia.org/wiki/Benevolent_dictator_for_life

View File

@ -0,0 +1,79 @@
Git and release versioning
==========================
This document covers how the Git repository is organized, as well as how
release numbers are organized.
Releases
--------
Cahute's main source package distribution, also known as "tarballs", is
on `ftp.cahuteproject.org/releases
<https://ftp.cahuteproject.org/releases>`_.
Cahute uses `Semantic Versioning` with only major and minor versions, not
patches. "Breaking changes" cover both the library and utilities, i.e.:
* If a valid usage of the library becomes invalid or produces an unexpected
result (e.g. requests more privileges), breaking changes have occurred.
* If a valid usage of any of the command-line utilities become invalid or
produce an unexpected result, breaking changes have occurred.
Note however that **0.x versions don't require backwards compatible changes
between minor versions**, only 1.0 and above, due to API incremental design
and stabilization concerns.
.. _release-process:
Release process
~~~~~~~~~~~~~~~
Only the BDFL can release a new version, by pushing a new commit
``feat: release version x.y`` on ``develop`` that:
* Updates the project version in the ``project()`` function call in
``CMakeLists.txt``.
* Updates the project version with the ``version`` attribute of the
``docs/conf.py`` file.
The BDFL then pushes a tag containing the version name, e.g. ``x.y`` in this
case, and the CI, on detecting a new tag:
* Creates a tarball of the Git repository, and pushes it to the
tarball distribution point with an associated SHA256 checksum file.
* Builds the documentation and updates the main website using the newly
generated files.
Git repository structure
------------------------
Cahute's main repository is at `gitlab.com/cahuteproject/cahute
<https://gitlab.com/cahuteproject/cahute>`_.
**Only maintainers are allowed to push to it**; other contributors must work
on a fork, then submit the remote branch as a merge request.
The main branch is ``develop``, which may have breaking changes at any point.
At every release, a tag is created, pointing on the commit that has
incremented the version; see :ref:`release-process` for more information.
If contributions, under the form of merge requests, are accepted, they are
to be merged using fast-forward with no merge commit.
Maintainers are allowed to push their work on the repository under branches
prefixed by one of the following:
* ``fix/``: If a fix is required for an issue, e.g.
``fix/fix-bad-check-packet-type-on-special-mode``.
* ``feat/``: For adding features such as adding a protocol, a file format,
an option, and so on.
* ``refactor/``: For refactoring part of the code or documentation.
Git commit messages must be named using the commit naming convention;
see :ref:`project-commit-naming`.
.. note::
This structure, while simple, gives flexibility for release branches,
hotfix branches, and so on, to be introduced later.
.. _Semantic Versioning: https://semver.org/

4
docs/requirements.txt Normal file
View File

@ -0,0 +1,4 @@
Sphinx~=7.2
sphinx-autobuild
sphinxcontrib-mermaid
furo

14
docs/topics.rst Normal file
View File

@ -0,0 +1,14 @@
Discussion topics
=================
In this section, we will explore discussion topics regarding the project's
environment and design. These topics provide keys to understanding what is
happening in this project, and why it is happening.
.. toctree::
:maxdepth: 2
topics/picture-formats
topics/protocols
topics/usb-detection
topics/logging

57
docs/topics/logging.rst Normal file
View File

@ -0,0 +1,57 @@
.. _logging:
Logging facilities with Cahute
==============================
Cahute has logging facilities for its internal needs, which functions with
logging levels.
Every log is accompanied with a level, among the following:
* ``info`` (:c:macro:`CAHUTE_LOGLEVEL_INFO`): debug messages, e.g. packet
contents or activated behaviour tweaks that have been encountered.
* ``warning`` (:c:macro:`CAHUTE_LOGLEVEL_WARNING`): messages signifying
abnormal behaviours that are recoverable and recovered by the library.
* ``error`` (:c:macro:`CAHUTE_LOGLEVEL_ERROR`): messages signifying abnormal
behaviours that are not recoverable, and may cause link termination, or
termination of a specific flow.
* ``fatal`` (:c:macro:`CAHUTE_LOGLEVEL_FATAL`): messages signifying program
termination, e.g. due to catastrophic system-related incidents.
Logging messages are printed on standard error if their level is allowed to
be printed.
.. note::
There is no way currently to reroute these logs to another output than
standard error. If the need arises, this may change.
Whether messages for a given logging level are printed or not can be
controlled from the outside, by setting the current "logging level".
Messages for a logging level can be printed if they are at the same or of
higher importance than the currently configured logging level for the
library, i.e. if they are on the same line or below in the list presented
above.
By that rationale:
* If the currently configured logging level is ``info``, then messages will
be displayed for all logging levels, since ``info`` has the lowest
importance.
* If the currently configured logging level is ``warning``, then messages
will be displayed for the ``warning``, ``error`` and ``fatal`` logging
level only.
* If the currently configured logging level is ``error``, then messages
will be displayed for the ``error`` and ``fatal`` logging levels only.
* If the currently configured logging level is ``fatal``, then messages
will be displayed for the ``fatal`` logging level only.
There is a special logging level ``none`` (:c:macro:`CAHUTE_LOGLEVEL_NONE`)
that, if configured, will not let any log be printed.
As a library user, the current logging level can be gathered using
:c:func:`cahute_get_log_level`, and can be set using
:c:func:`cahute_set_log_level`.
As a command-line user, some command-line utilities support an ``-l`` and/or
``--log`` option to set the current logging level for the library.

View File

@ -0,0 +1,119 @@
Picture formats
===============
This document describes the various picture formats used by CASIO or other
software surrounding its calculators in protocols and file formats.
.. _picture-format-1bit:
1bpp monochrome picture format
------------------------------
This format is the basic frame format for fx-9860G calculators and compatible.
It is used for internal VRAM representations, as well as for screenstreaming.
In this format, every pixel is represented as a bit, i.e. one byte contains
8 pixels. An off bit (``0b0``) represents a white pixel, and an
on bit (``0b1``) represents a black pixel.
The picture is organized by row first, column second.
If the width is not divisible by 8, then the last bits of the last
byte of the row are unused (fill bits), and the next row starts at the
beginning of the next byte.
For computing the size of such pictures, one must compute the number of bytes
a row occupies (usually ``ceil(width / 8)``), then multiply it by the number
of rows.
In Cahute, this format is represented by
:c:macro:`CAHUTE_PICTURE_FORMAT_1BIT_MONO`.
.. _picture-format-2bit-dual:
Dual 1bpp gray picture format
-----------------------------
This format is one of the possible screen streaming formats for fx-CG
(Prizm) calculators, using the ``1RM2`` algorithm.
This format is basically two pictures using :ref:`picture-format-1bit`
placed back-to-back (with alignment), where the obtained colours are
the following:
.. list-table::
:header-rows: 1
* - Bit from picture 1/2
- Bit from picture 2/2
- Obtained colour (``0xRRGGBB``)
* - ``0b0``
- ``0b0``
- ``0xFFFFFF``
* - ``0b0``
- ``0b1``
- ``0xAAAAAA``
* - ``0b1``
- ``0b0``
- ``0x777777``
* - ``0b1``
- ``0b1``
- ``0x000000``
For computing the size of such pictures, one must compute the number of bytes
a picture occupies, and multiply it by two, i.e. ``2 * ceil(width / 8)``.
In Cahute, this format is represented by
:c:macro:`CAHUTE_PICTURE_FORMAT_1BIT_DUAL`.
.. _picture-format-4bit-rgb-packed:
4bpp packed RGB picture format
------------------------------
This format is one of the possible screen streaming formats for fx-CG
(Prizm) calculators, using the ``1RC3`` algorithm.
In this format, every pixel is represented by a nibble (group of
4 consecutive bits), where, from high to low order:
- If the first bit is on (``0b1``), then the red component is set.
- If the next bit is on (``0b1``), then the green component is set.
- If the next bit is on (``0b1``), then the blue component is set.
- The last bit is an alignment bit, and can be ignored.
The picture is organized by row first, column second.
If the width is not divisible by 2, then the last pixel of every odd row
and the first pixel of every even row share the same byte, and if the height
is also odd, then the last 4 bits of the picture will be present as alignment.
For computing the size of such pictures, one must compute the number of
pixels, divide it by two and round to the next integer, i.e.
``ceil(width * height / 2)``.
In Cahute, this format is represented by
:c:macro:`CAHUTE_PICTURE_FORMAT_4BIT_RGB_PACKED`.
.. _picture-format-r5g6b5:
R5G6B5 picture format
---------------------
This format is the basic frame format for fx-CG (Prizm) calculators.
It is used for internal VRAM representations, as well as for screenstreaming
using the ``1RC2`` algorithm.
In this format, every pixel is represented by a 16-bit integer represented
using big endian, where, from high to low order:
- The first 5 bits represent the high 5 bits of the red component.
- The next 6 bits represent the high 6 bits of the green component.
- The last 5 bits represent the high 5 bits of the blue component.
The picture is organized by row first, column second.
The size of such pictures is the number of pixels multiplied by 2.
In Cahute, this format is represented by
:c:macro:`CAHUTE_PICTURE_FORMAT_16BIT_R5G6B5`.

12
docs/topics/protocols.rst Normal file
View File

@ -0,0 +1,12 @@
Communication protocols
=======================
.. toctree::
:maxdepth: 2
protocols/cas40
protocols/cas50
protocols/cas100
protocols/seven
protocols/seven-ohp
protocols/ums

View File

@ -0,0 +1,10 @@
.. _protocol-cas100:
CAS100 -- Serial protocol implemented by CASIO AFX calculators
===============================================================
This protocol is used by AFX / Graph 100 calculators, produced in the
early 2000s. It uses 50 bytes long headers at double the default speed
from CAS50, 19200 bauds instead of 9600 bauds.
.. todo:: More information here.

View File

@ -0,0 +1,14 @@
.. _protocol-cas40:
CAS40 -- Serial protocol with 40 bytes long packets
===================================================
This protocol is used on serial links with calculators from the early 1990s
era.
.. todo:: More information here.
.. toctree::
:maxdepth: 2
cas40/packet-format

View File

@ -0,0 +1,40 @@
CAS40 packet format
===================
The header format for a CAS40 packet is the following:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 0 (0x00)
- 2 B
- Type (*FT*)
- File type.
- See following sections.
* - 2 (0x02)
- 5 B
- Type-specific data (*TD*).
- Binary type-specific data.
- ``\x00\x01\x00\x00\x00`` for a 0x100-long program.
* - 7 (0x07)
- 12 B
- File name (*FN*)
- Name of the file for an editor program.
- ``HELLO\xFF\xFF\xFF\xFF\xFF\xFF\xFF``
* - 19 (0x13)
- 12 B
- File password (*FP*)
- Password of the file for an editor program.
- ``WORLD\xFF\xFF\xFF\xFF\xFF\xFF\xFF``
* - 31 (0x1F)
- 7 B
- Reserved
- Should be set to ``\xFF``
- ``\xFF\xFF\xFF\xFF\xFF...``
.. todo:: Describe file types here?

View File

@ -0,0 +1,17 @@
.. _protocol-cas50:
CAS50 -- Serial protocol with 50 bytes long packets
===================================================
This protocol is used on serial links with calculators from the late 1990s
era, and early 2000s era excluding AFX.
It is mostly used on serial links, with a default configuration of 9600 bauds,
no parity and 1 stop bit.
.. todo:: More information here.
.. toctree::
:maxdepth: 2
cas50/packet-format

View File

@ -0,0 +1,179 @@
CAS50 packet format
===================
All CAS50 packets are introduced by a single byte, which defines the basic
purpose of the packet and the format and size of the payload following it.
.. list-table::
:header-rows: 1
* - Value
- Name
- Description
- Payload
* - ``0x06``
- ``ACKNOWLEDGE``
- The receiver accepts the data packet, confirms overwrite, or the
sender confirms overwrite.
- *(none)*
* - ``0x13``
- ``ESTABLISHED``
- The receiver acknowledges connection establishment.
- *(none)*
* - ``0x15``
- ``ABORT``
- The sender requests an interactive connection, or aborts overwrite
if an overwrite confirmation is requested.
- *(none)*
* - ``0x16``
- ``START``
- The sender requests a non-interactive connection.
- *(none)*
* - ``0x21``
- ``ALREADY_EXISTS``
- The receiver requests overwrite confirmation from the sender.
- *(none)*
* - ``0x24`` (or ``0x00``)
- ``INVALID_DATA_TYPE``
- The receiver rejects the data packet due to an invalid data type,
or because it is out of memory.
- *(none)*
* - ``0x2b``
- ``INVALID_CHECKSUM``
- The received rejects the packet due to an invalid checksum.
The transfer aborts.
- *(none)*
* - ``0x3a``
- ``DATA``
- Data payload.
- :ref:`cas50-data-packets`
* - ``0x51``
- ``INVALID_DATA``
- The receiver aborts the transfer due to errors in the header.
- *(none)*
.. _cas50-data-packets:
Data packet payload
-------------------
Data packet payloads, with the notable exception of the type (*T*) field which
is specific to the protocol, mirror the internal format in the calculator's
main memory, including the header.
The format of the payload header is the following:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 0 (0x00)
- 4 B
- Type (*T*)
- Basic purpose of the packet
- ``END\0``
* - 2 (0x02)
- 2 B
- File Type (*FT*)
- File type, used by ``TXT`` packets.
- ``PG``
* - 4 (0x04)
- 4 B
- File Size (*FS*)
- Size of the data accompanying the header (big endian).
- ``\x00\x00\x00\xFF``
* - 8 (0x08)
- 8 B
- File Name (*FN*)
- Name of the file, with unset bytes being set to ``\xFF``.
- ``HELLO\xFF\xFF\xFF``
* - 16 (0x10)
- 8 B
- Alternative File Type (*AFT*)
- Alternative file type used for some packet types, notably variables.
- ``VariableR\x0A``
* - 24 (0x18)
- 8 B
- File Password (*FP*)
- Password of the file, with unset bytes being set to ``\xFF``.
- ``WORLD\xFF\xFF\xFF``
* - 32 (0x20)
- 2 B
- Base, if the file is a program.
- ``BN`` for Base programs, ``NL`` otherwise.
- ``BN``
* - 34 (0x22)
- 6 B
- Backup Size (*BS*) *(?)*
- Size of the backup (big endian).
- ``\x00\x10\x00\x00\x00\x00``
* - 40 (0x28)
- 6 B
- (unknown)
- Unknown, filled with ``\xFF``.
- ``\xFF\xFF\xFF\xFF\xFF\xFF``
* - 48 (0x30)
- 2 B
- Checksum (*CS*)
- Checksum (big endian).
- ``\x12\x34``
Note that any field not used by the packet type should be set to ``\xFF``.
``END\xFF`` packets
~~~~~~~~~~~~~~~~~~~
.. todo:: Describe the packet's role.
All fields other than the type aren't used, and should be set to ``\xFF``.
``TXT\0`` packets
~~~~~~~~~~~~~~~~~
Such packets carry over a main memory textual file.
.. list-table::
:header-rows: 1
* - Subtype (*ST*) value
- Description
* - ``PG``
- Program.
``IMG\0`` packets
~~~~~~~~~~~~~~~~~
Such packets carry over a main memory picture file.
.. list-table::
:header-rows: 1
* - Subtype (*ST*) value
- Description
* - ``PC``
- Picture.
``MEM\0`` packets
~~~~~~~~~~~~~~~~~
Such packets carry over a backup.
.. list-table::
:header-rows: 1
* - Subtype (*ST*) value
- Description
* - ``BU``
- Backup.
``VAL\0`` packets
~~~~~~~~~~~~~~~~~
Such packets carry over a variable. Particularities for this packet are:
* *FN* should be set to the variable name (?).
* *AFT* should be set to ``VariableR\x0A``.

View File

@ -0,0 +1,31 @@
.. _protocol-seven-ohp:
Protocol 7.00 Screenstreaming -- fx-9860G and fx-CG screenstreaming
===================================================================
Screenstreaming, also named "OverHead Projector" (OHP), "Projector", or
"Screen Receiver"\ [#screenreceiver]_, is a mode in which the calculator
shares its screen contents to another device.
This protocol is analogous to Protocol 7.00, see :ref:`protocol-seven` for
more information. It is used on the same models and during the same period
of time.
However, it has its own packet formats and flows that make it effectively
a completely separate protocol, sharing only a few similarities. Therefore,
it is documented in a completely separated set of sections and documents
from the original protocol, for clarity.
.. toctree::
:maxdepth: 1
seven-ohp/packet-format
seven-ohp/flows
.. [#screenreceiver] `Screen Receiver`_ is the name of the piece of software
by CASIO to view the calculator's screen from a desktop PC running on
MacOS, OS X or Microsoft Windows.
.. _Screen Receiver:
https://www.planet-casio.com/Fr/logiciels/voir_un_logiciel_casio.php
?showid=102

View File

@ -0,0 +1,57 @@
Protocol 7.00 Screenstreaming communication flows
=================================================
Screenstreaming with Protocol 7.00 involves a **sender** and a **receiver**.
The objective with this protocol is for the sender to be able to
send frames as they are produced to the receiver.
Except for a conditional acknowledgement at the beginning of the flow,
the receiver does not send anything, and **in case of invalid checksum,
it discards such packets**.
The communication schema is the following:
.. mermaid::
sequenceDiagram
Participant sender as Sender<br />(Calculator)
Participant receiver as Receiver<br />(PC, ...)
alt Sender requires acknowledgement from the Receiver
sender->>receiver: Check packet
receiver->>sender: Acknowledgement packet
end
loop Screenstreaming is active
sender->>receiver: Frame packet
end
.. warning::
Sometimes, due to the high volume of data, a few bytes can be skipped
here and there, leading to a desynchronization on the input stream.
It is recommended, on the receiving function, to add an option to
find the beginning of the next screenstreaming packet, to fix such
desynchronized inputs.
.. _seven-ohp-acknowledge:
Acknowledge a Sender
--------------------
On certain conditions, such as when the ``ScreenRecv(XP)`` mode is used on
the fx-CG50, the sender requires acknowledgement from the receiver.
As such, it will continuously send check packets, such as the following one::
16 43 41 4c 30 30 44 30 .CAL00D0
In such cases, the receiver must answer with the following acknowledgement
packet::
06 30 32 30 30 31 30 44 .020010D
Once the sender has received acknowledgement, it will start sending
frame packets.
For more information, see :ref:`seven-ohp-check-packet` and
:ref:`seven-ohp-ack-packet`.

View File

@ -0,0 +1,227 @@
Protocol 7.00 Screenstreaming packet format
===========================================
This document defines the packet formats for Protocol 7.00 Screenstreaming.
.. note::
In places, most notably in some subheaders and with the checksum, the
protocol uses the same ASCII-HEX encoding as for Protocol 7.00;
see :ref:`seven-ascii-hex` for more information.
Fields represented using ASCII-HEX will reference the section from
Protocol 7.00 packet format directly.
All packets with Protocol 7.00 Screenstreaming packet have the
The packet mode for this mode is the following:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 0 (0x00)
- 1 B
- Type (*T*)
- Basic purpose of the packet
- 0x00 to 0x1F
* - 1 (0x01)
- 5 B
- Subtype (*ST*)
- Subtype for the packet.
- 5-char ASCII string depending on the type.
* - 6 (0x06)
- *N* B
- Payload (*D*)
- Payload, for which the format depends on the format.
-
* - 6 + *N* (0x06 + *N*)
- 2 B
- Checksum (*CS*)
- Checksum for the packet.
- 2-char :ref:`seven-ascii-hex` value, e.g. ``5B``.
The checksum is computed :ref:`the same way as for Protocol 7.00
<seven-checksum>`, that is to say, it is obtained by summing all bytes from
*ST* to *D* included (i.e. the entire packet except *T* and *CS*), and
adding 1 to its bitwise complement.
The interpretation of the packet depends on the value of *T*.
See the following sections for more information.
.. note::
The header does not specify the total size of the packet, which implies
that it must be computed using the type, subtype and eventual subheader.
This, unfortunately, makes it more susceptible to corruption.
.. _seven-ohp-ack-packet:
``0x06`` -- Acknowledgement packet
----------------------------------
The acknowledgement packet can be used to accept a synchronization, so that
the sender starts sending frame packets instead of check packets. See
:ref:`seven-ohp-acknowledge` for more information.
The only known subtype of this packet is ``02001``.
.. _seven-ohp-frame-packet:
``0x0B`` -- Frame packet
------------------------
The frame packet contains a frame of the screen.
``TYP01`` frame format
~~~~~~~~~~~~~~~~~~~~~~
Such packets are sent by the fx-9860G and compatible on screenstreaming
mode, and are of the following format:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 6 (0x06)
- 1024 B
- Frame (*F*)
- Frame data, as a 128x64 1-bit monochrome picture; see
:ref:`picture-format-1bit` for more information.
-
.. _seven-screen-typz1:
``TYPZ1`` frame format
~~~~~~~~~~~~~~~~~~~~~~
Such packets are sent by the fx-CG series on screenstreaming modes, and are
of the following format:
.. warning::
In this table, the offset starts at 6 instead of 0.
The subheader itself is only 18 bytes long!
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 6 (0x06)
- 6 B
- Frame Length (*FL*)
- Length of the *F* field.
- 6-char :ref:`seven-ascii-hex` value, e.g. ``001234``.
* - 12 (0x0C)
- 4 B
- Height (*H*)
- Height of the frame, in pixels.
- 4-char :ref:`seven-ascii-hex` value, e.g. ``0123``.
* - 16 (0x10)
- 4 B
- Width (*W*)
- Width of the frame, in pixels.
- 4-char :ref:`seven-ascii-hex` value, e.g. ``0123``.
* - 20 (0x14)
- 4B
- Frame Format (*FF*)
- Format of the frame, i.e. picture encoding in use.
- ``1RC2``, ``1RC3``, ``1RM2``
* - 24 (0x18)
- *FL* B
- Frame (*F*)
- Frame data.
-
The frame formats are the following:
.. list-table::
:header-rows: 1
* - *FF* field value
- Format
- Expected size
* - ``1RC2``
- :ref:`picture-format-r5g6b5`
- ``width * height * 2``
* - ``1RC3``
- :ref:`picture-format-4bit-rgb-packed`
- ``ceil(width * height / 2)``
* - ``1RM2``
- :ref:`picture-format-2bit-dual`
- ``2 * height * ceil(width / 8)``
``TYPZ2`` frame format
~~~~~~~~~~~~~~~~~~~~~~
Such packets can also be sent by the fx-CG series on screenstreaming modes,
and are of the following format:
.. warning::
In this table, the offset starts at 6 instead of 0.
The subheader itself is only 20 bytes long!
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 6 (0x06)
- 8 B
- Frame Length (*FL*)
- Length of the *F* field.
- 8-char :ref:`seven-ascii-hex` value, e.g. ``00001234``.
* - 14 (0x0E)
- 4 B
- Height (*H*)
- Height of the frame, in pixels.
- 4-char :ref:`seven-ascii-hex` value, e.g. ``0123``.
* - 18 (0x12)
- 4 B
- Width (*W*)
- Width of the frame, in pixels.
- 4-char :ref:`seven-ascii-hex` value, e.g. ``0123``.
* - 22 (0x16)
- 4B
- Frame Format (*FF*)
- Format of the frame, i.e. picture encoding in use.
- ``1RC2``, ``1RC3``, ``1RM2``
* - 26 (0x1A)
- *FL* B
- Frame (*F*)
- Frame data.
-
.. note::
This is roughly equivalent to the :ref:`seven-screen-typz1`, with
the *FL* field having been changed from 6 to 8 chars to allow
larger frames.
The frame formats are the same as for :ref:`seven-screen-typz1`.
.. _seven-ohp-check-packet:
``0x16`` -- Check packet
------------------------
Check packets are sent by the sender to request an acknowledgement from the
receiver. See :ref:`seven-ohp-acknowledge` for more information.
The only known subtype for this mode is ``CAL00``.

View File

@ -0,0 +1,23 @@
.. _protocol-seven:
Protocol 7.00 -- Serial and USB protocol used by post fx-9860G calculators
==========================================================================
This protocol is used by calculators starting from the fx-9860G, published
in 2004, up to the current day.
.. note::
fx-CG calculators and derivatives still use a derivative from Protocol
7.00, although not directly, but hidden behind proprietary SCSI commands.
See :ref:`protocol-ums` for more information.
.. toctree::
:maxdepth: 1
seven/specific-encodings
seven/packet-format
seven/flows
seven/casio-commands
seven/use-cases
seven/hardware-identifiers

View File

@ -0,0 +1,756 @@
Known Protocol 7.00 commands by CASIO
=====================================
These commands have been defined by CASIO through their Protocol 7.00
implementations. Some may no longer be implemented on newer models.
.. _seven-command-00:
``00`` "Restart"
----------------
.. seven-command::
:code: 00
Restart or reset the device.
.. _seven-command-01:
``01`` "Get device information"
-------------------------------
.. seven-command::
:code: 01
Request an extended ACK with subtype ``02``, and a payload containing
device information.
See :ref:`seven-get-device-information` for the related flow, and
:ref:`seven-ack-packet` for more information regarding the payload format.
.. _seven-command-02:
``02`` "Set link settings"
--------------------------
.. seven-command::
:code: 02
:d1: Baud rate in decimal format.
:d1-example: 19200
:d2: Parity, among "ODD", "EVEN" or "NONE".
:d2-example: EVEN
:d3: Stop bits, "1" or "2".
:d3-example: 2
If connected to the passive device using a serial link, this command
signifies a request to communicate using different serial parameters.
.. note::
The serial speeds are limited to the following speeds (or baud rates):
300, 600, 1200, 2400, 4800, 9600 (*by default*), 19200, 38400, 57600
and 115200 bauds.
See :ref:`seven-update-serial-params` for more information.
.. _seven-command-06:
``06`` "Set link timeout"
-------------------------
.. seven-command::
:code: 06
:d1: 4-char :ref:`seven-ascii-hex` number of minutes
:d1-example: 0001
Set the timeout for the link, after which the connection is terminated.
.. todo:: Does the calculator send a termination packet?
.. _seven-command-07:
``07`` Verification 1 (OS Update)
---------------------------------
.. todo:: Describe this command.
.. _seven-command-08:
``08`` Verification 2 (OS Update)
---------------------------------
.. todo:: Describe this command.
.. _seven-command-09:
``09`` Verification 3 (OS Update)
---------------------------------
.. todo:: Describe this command.
.. _seven-command-0A:
``0A`` Verification 4 (OS Update)
---------------------------------
.. todo:: Describe this command.
.. _seven-command-20:
``20`` "Create directory" (main memory)
---------------------------------------
.. seven-command::
:code: 20
:d1: Name of the directory to create.
:d1-example: HELLO
Create a directory on the main memory root.
.. _seven-command-21:
``21`` "Delete directory" (main memory)
---------------------------------------
.. seven-command::
:code: 21
:d1: Name of the directory to delete.
:d1-example: HELLO
Delete a directory on the main memory root.
.. _seven-command-22:
``22`` "Rename directory" (main memory)
---------------------------------------
.. seven-command::
:code: 22
:d1: Name of the directory to rename.
:d1-example: HELLO
:d2: New name for the directory.
:d2-example: WORLD
Rename a directory on the main memory root.
.. _seven-command-23:
``23`` "Change working directory" (main memory)
-----------------------------------------------
.. seven-command::
:code: 23
:d1: Name of the directory (or "" for root).
:d1-example: HELLO
Change the working directory on the main memory.
.. note::
When provided and not empty, the directory name is always from the
root, since the main memory doesn't support more depth.
.. _seven-command-24:
``24`` "Request file transfer" (main memory)
--------------------------------------------
.. seven-command::
:code: 24
:dt: Main memory data type.
:dt-example: 01
:d1: Directory name.
:d1-example: system
:d2: File name.
:d2-example: PLSOUMOI
:d3: Group name.
Request a main memory file to be transferred, using command
:ref:`seven-command-25`.
.. _seven-command-25:
``25`` "Transfer file" (main memory)
------------------------------------
.. seven-command::
:code: 25
:ow: Overwrite mode.
:dt: Main memory data type.
:dt-example: 01
:fs: File size.
:fs-example: 00012345
:d1: Directory name.
:d1-example: system
:d2: File name.
:d2-example: PLSOUMOI
:d3: Group name.
Transfer a main memory file.
.. _seven-command-26:
``26`` "Delete file" (main memory)
----------------------------------
.. seven-command::
:code: 26
:dt: Main memory data type.
:dt-example: 01
:fs: File size.
:fs-example: 00012345
:d1: Directory name.
:d1-example: system
:d2: File name.
:d2-example: PLSOUMOI
:d3: Group name.
Delete a main memory file.
.. _seven-command-27:
``27`` "Rename file" (main memory)
----------------------------------
.. seven-command::
:code: 27
:dt: Main memory data type.
:dt-example: 01
:d1: Directory name.
:d1-example: system
:d2: File name.
:d2-example: PLSOUMOI
:d3: New file name.
:d3-example: PLUSMOIN
Rename a main memory file.
.. _seven-command-28:
``28`` "Copy file" (main memory)
--------------------------------
.. seven-command::
:code: 28
:dt: Main memory data type.
:dt-example: 01
:d1: Directory name.
:d1-example: system
:d2: File name.
:d2-example: PLUSMOIN
:d3: New directory name.
:d3-example: system
:d4: New file name.
:d4-example: PLMNCOPY
Copy a main memory file into another on the device.
.. _seven-command-29:
``29`` "Request transfer of all files" (main memory)
----------------------------------------------------
.. seven-command::
:code: 29
Request all main memory files to be transferred using command
:ref:`seven-command-25`.
.. _seven-command-2A:
``2A`` "Reset" (main memory)
----------------------------
.. seven-command::
:code: 2A
Reset main memory.
.. _seven-command-2B:
``2B`` "Request available capacity" (main memory)
-------------------------------------------------
.. seven-command::
:code: 2B
Request the available capacity on the main memory to be transferred
using command :ref:`seven-command-2C`.
.. _seven-command-2C:
``2C`` "Transfer available capacity" (main memory)
--------------------------------------------------
.. seven-command::
:code: 2C
:fs: Available capacity.
:fs-example: 00123456
Transfer the available capacity on the main memory.
.. _seven-command-2D:
``2D`` "Request all file information" (main memory)
---------------------------------------------------
.. seven-command::
:code: 2D
Transfer information regarding all main memory files, using
command :ref:`seven-command-2E`.
.. _seven-command-2E:
``2E`` "Transfer file information" (main memory)
------------------------------------------------
.. seven-command::
:code: 2E
:dt: Main memory data type.
:dt-example: 01
:fs: File size.
:fs-example: 00012345
:d1: Directory name.
:d1-example: system
:d2: File name.
:d2-example: PLUSMOIN
:d3: Group name.
Transfer information regarding a main memory file.
.. _seven-command-2F:
``2F`` "Request raw main memory"
--------------------------------
.. seven-command::
:code: 2F
Request the raw main memory to be sent using command
:ref:`seven-command-30`.
.. _seven-command-30:
``30`` "Transfer raw main memory"
---------------------------------
.. seven-command::
:code: 30
Transfer the raw main memory.
.. _seven-command-31:
``31`` "Request setup entry"
----------------------------
.. seven-command::
:code: 31
:d1: Setup entry name.
Request a setup entry, to be sent using command :ref:`seven-command-32`.
.. _seven-command-32:
``32`` "Transfer setup entry"
-----------------------------
.. seven-command::
:code: 32
:d1: Setup entry name.
:d1-example: Angle
:d2: Setup entry value.
:d2-example: 01
Send a setup entry.
.. _seven-command-33:
``33`` "Request all setup entries"
----------------------------------
.. seven-command::
:code: 33
Request all setup entries to be sent using command
:ref:`seven-command-32`.
.. _seven-command-40:
``40`` "Create directory" (storage)
-----------------------------------
.. seven-command::
:code: 40
:d1: Directory name.
:d1-example: MYFOLDER
:d5: Device name.
:d5-example: fls0
Create a directory on the provided storage device.
.. _seven-command-41:
``41`` "Delete directory" (storage)
-----------------------------------
.. seven-command::
:code: 41
:d1: Directory name.
:d1-example: MYFOLDER
:d5: Device name.
:d5-example: fls0
Delete a directory on the provided storage device.
.. _seven-command-42:
``42`` "Rename directory" (storage)
-----------------------------------
.. seven-command::
:code: 42
:d1: Directory name.
:d1-example: MYFOLDER
:d2: New directory name.
:d2-example: PRECIOUS
:d5: Device name.
:d5-example: fls0
Rename a directory on the provided storage device.
.. _seven-command-43:
``43`` "Change working directory" (storage)
-------------------------------------------
.. seven-command::
:code: 43
:d1: Directory name.
:d1-example: MYFOLDER
:d5: Device name.
:d5-example: fls0
Update the working directory on the given device.
.. _seven-command-44:
``44`` "Request file" (storage)
-------------------------------
.. seven-command::
:code: 44
:d1: Directory name.
:d1-example: MYFOLDER
:d2: File name.
:d2-example: MYADDIN.G1A
:d5: Device name.
:d5-example: fls0
Request for a file to be sent on the given device.
This command is used in the following use cases:
* :ref:`seven-request-file-from-storage`.
.. _seven-command-45:
``45`` "Transfer file" (storage)
--------------------------------
.. seven-command::
:code: 45
:ow: Overwrite mode.
:ow-example: 02
:fs: File size.
:fs-example: 00123456
:d1: Directory name.
:d2: File name.
:d2-example: MYADDIN.G1A
:d5: Device name.
:d5-example: fls0
Transfer a file to be sent on the given device.
This command is used in the following use cases:
* :ref:`seven-send-file-to-storage`;
* :ref:`seven-request-file-from-storage`.
.. _seven-command-46:
``46`` "Delete file" (storage)
------------------------------
.. seven-command::
:code: 46
:d1: Directory name.
:d2: File name.
:d2-example: MYADDIN.G1A
:d5: Device name.
:d5-example: fls0
Delete a file on the given device.
This command is used in the following use cases:
* :ref:`seven-delete-file-on-storage`.
.. _seven-command-47:
``47`` "Rename file" (storage)
------------------------------
.. seven-command::
:code: 47
:d1: Directory name.
:d2: File name.
:d2-example: MYADDIN.G1A
:d3: New file name.
:d3-example: GRAVDUCK.G1A
:d5: Device name.
:d5-example: fls0
Rename a file on the given device.
.. _seven-command-48:
``48`` "Copy file" (storage)
----------------------------
.. seven-command::
:code: 48
:d1: Directory name.
:d2: File name.
:d2-example: ORIGNAME.G1A
:d3: New directory name.
:d4: New file name.
:d4-example: NEWNAME.G1A
:d5: Device name.
:d5-example: fls0
Copy a file on the given device.
This command is used in the following use cases:
* :ref:`seven-copy-file-on-storage`.
.. _seven-command-49:
``49`` "Request all files" (storage)
------------------------------------
.. seven-command::
:code: 49
:d5: Storage device
:d5-example: fls0
Request all files to be transmitted using command
:ref:`seven-command-45`.
.. _seven-command-4A:
``4A`` "Reset" (storage)
------------------------
.. seven-command::
:code: 4A
:d5: Storage device
:d5-example: fls0
Reset a storage device.
This command is used in the following use cases:
* :ref:`seven-reset-storage`.
.. _seven-command-4B:
``4B`` "Request available capacity" (storage)
---------------------------------------------
.. seven-command::
:code: 4B
:d5: Storage device
:d5-example: fls0
Request the available capacity for a given storage device.
.. _seven-command-4C:
``4C`` "Transfer available capacity" (storage)
----------------------------------------------
.. seven-command::
:code: 4C
:fs: Available capacity
:d5: Storage device
:d5-example: fls0
Provide the available capacity for a given storage device.
.. _seven-command-4D:
``4D`` "Request all file information" (storage)
-----------------------------------------------
.. seven-command::
:code: 4D
:d5: Storage device
:d5-example: fls0
Request information from all files on the given storage device
to be transferred.
This command is used in the following use cases:
* :ref:`seven-list-files-on-storage`.
.. _seven-command-4E:
``4E`` "Transfer file information" (storage)
--------------------------------------------
.. seven-command::
:code: 4E
:fs: File size
:d1: Directory name
:d2: File name
:d2-example: MYADDIN.G1A
:d5: Storage device
:d5-example: fls0
Transfer information regarding a file.
This command is used in the following use cases:
* :ref:`seven-list-files-on-storage`.
.. _seven-command-4F:
``4F`` "Request flash image"
----------------------------
.. todo:: Describe this command.
.. _seven-command-50:
``50`` "Transfer flash image"
-----------------------------
.. todo:: Describe this command.
.. _seven-command-51:
``51`` "Optimize filesystem" (storage)
--------------------------------------
.. seven-command::
:code: 51
:d5: Storage device
:d5-example: fls0
Transfer information regarding a file.
This command is used in the following use cases:
* :ref:`seven-optimize-storage`.
.. _seven-command-52:
``52`` "Request CASIOWIN entry transfer"
----------------------------------------
.. seven-command::
:code: 52
Request the CASIOWIN entry to be transferred using command
:ref:`seven-command-53`.
.. _seven-command-53:
``53`` "Transfer CASIOWIN entry"
--------------------------------
.. seven-command::
:code: 53
Transfer the CASIOWIN entry.
.. _seven-command-54:
``54`` "Request bootcode transfer"
----------------------------------
.. seven-command::
:code: 54
Request the bootcode to be transferred using command
:ref:`seven-command-55`.
.. _seven-command-55:
``55`` "Transfer bootcode"
--------------------------
.. seven-command::
:code: 55
Transfer the bootcode.
.. _seven-command-56:
``56`` "Upload and run"
-----------------------
Upload and run a specially crafted program.
.. warning::
Please do not use this command lightly, as uploading badly formatted
programs or the incorrect program for your calculator **can brick said
calculator**!
This command does not use the typical command payload, but a **custom 24-bytes
payload** for which the format is the following:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Name
- Description
- Values
* - 0 (0x00)
- 8 B
- Upload size
- Size of the payload to upload.
- 8-char :ref:`seven-ascii-hex` number.
* - 8 (0x08)
- 8 B
- Load address
- RAM address at which to load the provided payload.
- 8-char :ref:`seven-ascii-hex` number, usually set to
``88030000``.
* - 16 (0x10)
- 8 B
- Start address
- Address which to jump to after successful upload.
- 8-char :ref:`seven-ascii-hex` number, usually set to
``88030000``.
This command is used in the following use cases:
* :ref:`seven-upload-and-run`.
``59`` "Backup main memory" (fx-CG)
-----------------------------------
.. todo:: Describe this command, known as "save Backup.g3m".
``5A`` "Restore main memory" (fx-CG)
------------------------------------
.. todo:: Describe this command, known as "restore Backup.g3m".

View File

@ -0,0 +1,380 @@
Protocol 7.00 communication flows
=================================
Protocol 7.00 involves an **active side** and a **passive side**.
At the beginning of the communication, the passive side is usually the
calculator, and the active side is the client, e.g. a PC or another
calculator.
When a flow isn't currently in progress, the active side is allowed to send
a command to the passive side. This command may start a flow where a
"role swap" occurs, i.e. the active side becomes the passive side, and vice
versa. At the end of said flow, the role are usually swapped again, so that
the original active side becomes the active side again.
When required, the role swap is always initiated by the active side.
The communication schema is the following:
.. mermaid::
sequenceDiagram
Participant active as Original Active Side<br>(PC, ...)
Participant passive as Original Passive Side<br>(calculator)
Note over active,passive: Initialize the link
active->>passive: Command
alt Erroneous
passive->>active: Error
else
passive->>active: ACK
alt Data transfer is required
Note over active,passive: Data transfer
else
alt Data transfer must be requested by the active side
Note over active,passive: Data transfer request
end
end
end
Note over active,passive: Terminate the link
The different blocks are described in the following sections.
.. note::
If any packet is received with an invalid checksum, a specific flow
takes place for the sender to be able to understand the situation and
resend said packet.
This sub-flow is considered part of the "sending a packet" flow, and
must be treated within this logic.
See :ref:`seven-report-invalid-checksum` for more information.
.. note::
In between any of the packet sending steps, if the side expected to send
a packet takes too long to do so, a sub flow takes place to check if the
other side is still present.
See :ref:`seven-check-link` for more information.
.. _seven-report-invalid-checksum:
Reporting invalid checksums
---------------------------
In case a packet has an invalid checksum, it may mean that the packet has
been corrupted by the medium lying under the link. In such cases, a specific
flow takes place so that the sender of the packet can resend the packet.
An example flow where the active side sends the corrupted packet
is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command with invalid checksum
passive->>active: Error with '01' subtype
active->>passive: Same command with valid checksum
Note over active,passive: ...
An example flow where the passive side sends the corrupted packet is
the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command
passive->>active: ACK with invalid checksum
active->>passive: Error with '01' subtype
passive->>active: ACK with valid checksum
Note over active,passive: ...
.. _seven-init-link:
Initiating the link
-------------------
When opening a link to a calculator that hasn't received any packets yet,
the PC must initialize the link using this flow, which is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Initial check
passive->>active: ACK
The initial check packet is a check packet of subtype ``00``; see
:ref:`seven-check-packet` for more information.
.. _seven-terminate-link:
Terminating the link
--------------------
When closing a link to a calculator that we don't plan on sending packets
anymore, we are expected to terminate the link using the following flow:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Terminate
passive->>active: ACK
See :ref:`seven-terminate-packet` for more information on the terminate packet.
.. _seven-check-link:
Checking up on the link
-----------------------
In order to detect a "deaf" passive side, the active side must run the
following flow if inactive for more than 6 minutes:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Regular check
passive->>active: ACK
The regular check is a check packet with the ``01`` subtype; see
:ref:`seven-check-packet` for more information.
If this flow is not run and the connection is active for more than 6 minutes,
the passive side terminates the connection.
.. _seven-confirm-overwrite:
Confirming or rejecting overwrite
---------------------------------
Some commands, such as :ref:`seven-command-45`, may require overwrite
confirmation in case the file already exists on the calculator. In such
cases, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command with OW set to '00'
passive->>active: Overwrite confirmation requested
active->>active: Request user confirmation
alt User confirms overwrite
active->>passive: Confirm overwrite
passive->>active: Acknowledge
else
active->>passive: Deny overwrite
passive->>active: Acknowledge
end
The packets used in this sequence are the following:
* The overwrite confirmation request is represented as a NAK packet with
the ``02`` subtype; see :ref:`seven-nak-packet`.
* The overwrite confirmation is represented as an ACK packet with the
``01`` subtype; see :ref:`seven-ack-packet`.
* The overwrite denial is represented as a NAK packet with the ``03``
subtype; see :ref:`seven-nak-packet`.
* Both the overwrite confirmation acknowledgement and overwrite denial
acknowledgement are represented as an ACK packet with the ``00`` subtype;
see :ref:`seven-ack-packet`.
.. _seven-transmit-data:
Transferring data
-----------------
Say that an active and a passive side have agreed on a data exchange through
commands, and have already swapped if necessary. In order to transfer data,
the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
loop Data left to transmit
active->>passive: Data packet
passive->>active: ACK
end
Data packets are described in :ref:`seven-data-packet`.
.. note::
Note that the number of data packets must be known in advance, since all
data packets contain both the sequence number and the total sequence count,
e.g. "packet 51/128".
In order to know the number of packets an original buffer can take, due
to escaping concerns, it is highly recommended to consider that all packets
contain up to 256 bytes, except the last one that may contain less.
For example:
* 500 bytes will be represented as 2 data packets (one of 256 bytes,
one of 244 bytes).
* 512 bytes will be represented as 2 data packets (both of 256 bytes).
* 1055 bytes will be represented as 5 data packets (four of 256 bytes,
one of 31 bytes).
Packet shifting
~~~~~~~~~~~~~~~
Packet shifting is a technique discovered in 2017 that makes data transfers
faster. It consists in sending the next data packet before the acknowledgement
for the previous one is received.
The flow becomes the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: First data packet
loop Data left to transmit
active->>passive: Send data packet #N
passive->>active: ACK for data packet #N - 1
end
passive->>active: ACK for last data packet
.. warning::
This technique comes with its risks, especially the fact that it renders
the link non-recoverable in case of bad packet checksum while it is
in effect, since the packet correction flow assumes that no "normal"
packet is sent after the problematic packet (while we have already sent
the packet that comes after in the sequence).
In order to mitigate such risks while still employing the technique,
Cahute disables packet shifting on serial links, i.e. only enables it
on USB and UAS (SCSI) links.
.. _seven-request-transfer:
Requesting data transfer(s)
---------------------------
When requesting a file or some data generally from the calculator,
the usual flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Original Active Side
Participant passive as Original Passive Side
active->>passive: Command (transfer request)
passive->>active: ACK
active->>passive: Roleswap
Note over active,passive: Active side becomes passive, and vice versa
loop Transfer required
passive->>active: Command (transfer)
active->>passive: ACK
Note over active,passive: Data flow from calculator to PC
end
passive->>active: Roleswap
Note over active,passive: Original active side becomes active again
This applies to several use cases, but an example one is the PC requesting
a file from the calculator's flash memory. The transfer request command for
such a case is :ref:`seven-command-24`, and the transfer command emitted
by the calculator after the roleswap is :ref:`seven-command-25`.
.. note::
The transfer request can lead to multiple commands from the calculator,
e.g. with commands such as :ref:`seven-command-29` that will spawn
one command and associated data transmission by file in the main memory.
.. _seven-get-device-information:
Requesting device information
-----------------------------
While in the "requesting transfer" rationale, the flow to get the device
information using command :ref:`seven-command-01` is different from the
usual solution applied for this case:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '01'
passive->>active: EACK with device information
.. note::
This is likely to avoid a more complex flow with a roleswap, as described
in :ref:`seven-request-transfer`, or because the aforementioned flow did
not exist when this command was conceived.
.. _seven-update-serial-params:
Updating serial parameters
--------------------------
If the link is established on a serial stream, it is possible to negotiate
different serial parameters with the calculator dynamically using
:ref:`seven-command-02`, using the following flow:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '02'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
Note over active,passive: Both sides update their<br />serial parameters before<br />their next packet exchanges.
end
The only elements that can be updated are the serial speed in bauds, the
parity, and the number of stop bits (1 or 2).
The serial speeds are limited to the following speeds (or baud rates):
300, 600, 1200, 2400, 4800, 9600 (*by default*), 19200, 38400, 57600 and
115200 bauds.
.. warning::
Depending on the quality of the serial cable (or USB to serial cable
/ converter), higher speeds may cause more corruption to occur, causing
a lot of resends and hence, being less efficient than lower baud rates.

View File

@ -0,0 +1,78 @@
.. _seven-hardware-identifiers:
Known hardware identifiers for Protocol 7.00
============================================
All known Protocol 7.00 environments support command :ref:`seven-command-01`.
Several people have used this command to obtain information using their
calculator in different link modes; this document inventories the results.
Bootcodes
---------
.. list-table::
:header-rows: 1
* - Hardware identifier
- Devices
- Link
* - ``Gy362000``
- fx-7400GII, fx-9750G(II), fx-9860GII SD
- yes
* - ``Gy363000``
- fx-7400GII-2, fx-9750GII-2, fx-9860G(II(-2)), fx-9860G slim,
fx-9860GII-2, Graph 35+E, Graph 75+E
- no
* - ``Gy490000``
- fx-7400GII-2
- yes
Bootcodes do **not** support restart-related commands :ref:`seven-command-00`
and :ref:`seven-command-06`.
Bootcodes with ``Link`` support command :ref:`seven-command-02`.
Devices
-------
.. list-table::
:header-rows: 1
* - Hardware identifier
- Devices
- Backup
* - ``Gy362006``
- fx-9750GII, Graph 35+ (SH3)
- no
* - ``Gy362007``
- fx-9750GII-2, Graph 35+ (SH4), Graph 35+E (OS < 2.05)
- no
* - ``Gy36200F``
- Graph 35+E
- no
* - ``Gy363006``
- fx-9860GII, Graph 75 (SH3)
- yes
* - ``Gy363007``
- fx-9860GII-2, Graph 75 (SH4), Graph 95 (SH4)
- no
* - ``Gy36300F``
- Graph 75+E
- no
* - ``Ly755000``
- fx-CG20, Prizm
- no
All devices support main memory (:ref:`seven-command-20` to
:ref:`seven-command-33`) and reset related commands (:ref:`seven-command-00`
and :ref:`seven-command-06`).
If there is at least one filesystem, flash commands
(:ref:`seven-command-40` to :ref:`seven-command-4E`, and
:ref:`seven-command-51`) are supported.
Two calculators that have different filesystems may have the same hardware
identifier, so it cannot be used for this (e.g. Graph 75 and Graph 95, the
latter having an SD port).
Devices with ``Backup`` support backup commands (:ref:`seven-command-4F`,
:ref:`seven-command-50`, and :ref:`seven-command-52` to
:ref:`seven-command-55`).

View File

@ -0,0 +1,420 @@
Protocol 7.00 packet format
===========================
All packets with Protocol 7.00 have the following header:
.. list-table::
:header-rows: 1
* - Size
- Field name
- Description
- Values
* - 1 B
- Type (*T*)
- Basic purpose of the packet
- 0x00 to 0x1F
* - 2 B
- Subtype (*ST*)
- Specific function of the packet
- 2-chars :ref:`seven-ascii-hex` value, ``00`` to ``FF``
* - 1 B
- Extended (*EX*)
- Whether data is attached to the packet
- 1-char :ref:`seven-ascii-hex` value, ``0`` or ``1``
If the packet is extended, i.e. if *EX* is set to ``1``, the following
extension is appended to the header:
.. list-table::
:header-rows: 1
* - Size
- Field name
- Description
- Values
* - 4 B
- Data size (*DS*)
- Size of the *D* field
- 4-char :ref:`seven-ascii-hex` value, ``0000`` to ``FFFF``
* - *DS* B
- Data (*D*)
- Additional data
- :ref:`seven-5c-padding` encoded data
All packets have the following footer:
.. list-table::
:header-rows: 1
* - Size
- Field name
- Description
- Values
* - 2 B
- Checksum (*CS*)
- Checksum for integrity check
- 2-char :ref:`seven-ascii-hex` value, ``00`` to ``FF``
.. _seven-checksum:
The checksum can be obtained or verified by summing all bytes going from
*ST* to *D* if present, or *EX* if not, and adding 1 to its bitwise
complement.
The interpretation of the packet depends on the value of *T*.
See the following sections for more information.
``0x01`` -- Command packet
--------------------------
The command packet reflects an order given by the active side to the
passive side. The packet subtype (*ST*) contains the command code.
If the packet is extended, the packet data (*D*) is **usually** formatted
the following way:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 0 (0x00)
- 2 B
- Overwrite (*OW*)
- Mode of operation if a file exists.
- 2-char :ref:`seven-ascii-hex` value:
- ``00``: Request user confirmation.
- ``01``: Terminate if exists.
- ``02``: Force overwrite.
* - 2 (0x02)
- 2 B
- Data type (*DT*)
- Data type.
- 2-char :ref:`seven-ascii-hex` value, ``00``.
* - 4 (0x04)
- 8 B
- File size (*FS*)
- Size of the file being transferred.
- 8-char :ref:`seven-ascii-hex` value.
* - 12 (0x0C)
- 2 B
- Data size 1 (*SD1*)
- Size of *D1*
- 2-char :ref:`seven-ascii-hex` value.
* - 14 (0x0E)
- 2 B
- Data size 2 (*SD2*)
- Size of *D2*
- 2-char :ref:`seven-ascii-hex` value.
* - 16 (0x10)
- 2 B
- Data size 3 (*SD3*)
- Size of *D3*
- 2-char :ref:`seven-ascii-hex` value.
* - 18 (0x12)
- 2 B
- Data size 4 (*SD4*)
- Size of *D4*
- 2-char :ref:`seven-ascii-hex` value.
* - 20 (0x14)
- 2 B
- Data size 5 (*SD5*)
- Size of *D5*
- 2-char :ref:`seven-ascii-hex` value.
* - 22 (0x16)
- 2 B
- Data size 6 (*SD6*)
- Size of *D6*
- 2-char :ref:`seven-ascii-hex` value.
* - 24 (0x16)
- *SD1* B
- Data 1 (*D1*)
- First argument
-
* - 24 (0x16) + *SD1*
- *SD2* B
- Data 2 (*D2*)
- Second argument
-
* - 24 (0x16) + *SD1* + *SD2*
- *SD3* B
- Data 3 (*D3*)
- Third argument
-
* - 24 (0x16) + *SD1* + *SD2* + *SD3*
- *SD4* B
- Data 4 (*D4*)
- Fourth argument
-
* - 24 (0x16) + *SD1* + *SD2* + *SD3* + *SD4*
- *SD5* B
- Data 5 (*D5*)
- Fifth argument
-
* - 24 (0x16) + *SD1* + *SD2* + *SD3* + *SD4* + *SD5*
- *SD6* B
- Data 6 (*D6*)
- Sixth argument
-
.. warning::
There is an exception, i.e. a command that uses a different payload format:
command :ref:`seven-command-56`.
.. _seven-data-packet:
``0x02`` -- Data packet
-----------------------
The data packet is used to transfer "raw" data, as described in
:ref:`seven-transmit-data`.
The packet subtype (*ST*) must be the same as the subtype (*ST*) of the
command that has initiated the data transmission; e.g. if the data flow has
been initiated following command :ref:`seven-command-25`, all data packets
in the corresponding flow must bear the *ST* ``25``.
All data packets are expected to be extended (i.e. *EX* is set to ``1``).
The layout of *D* is the following:
.. list-table::
:header-rows: 1
* - Size
- Field name
- Description
- Values
* - 4 B
- Total number (*TN*)
- Total number of data packets in current transmission
- 4-char :ref:`seven-ascii-hex` value, ``0001`` to ``FFFF``.
* - 4 B
- Current number (*CN*)
- Current data packet number in current transmission
- 4-char :ref:`seven-ascii-hex` value, ``0001`` to ``FFFF``.
* - 0-512 B
- Contents (*DD*)
- Contents of data packet
-
Note that since 512 bytes is the maximum transmittable amount, and that
:ref:`seven-5c-padding` can only double the data size, the maximum size
of transmitted data within one data packet is 256 bytes.
``0x03`` -- Roleswap packet
---------------------------
The roleswap packet indicates a roleswap, and is used for transfer requests;
see :ref:`seven-request-transfer` for more information.
Such packets will never be extended, and will never have another subtype
than ``00``.
.. _seven-check-packet:
``0x05`` -- Check packet
------------------------
This covers two different packet types, depending on the subtype (*ST*):
* If *ST* is ``00``, this is an initial check packet used to establish
communication with the calculator. See :ref:`seven-init-link` for
more information.
* If *ST* is ``01``, this is a regular check packet used to check if
the link is still up with the calculator. See :ref:`seven-check-link`
for more information.
This packet is not expected to be extended, i.e. *EX* should be ``0``.
.. _seven-ack-packet:
``0x06`` -- Acknowledgement (ACK) packet
----------------------------------------
This covers two different packet types, depending on the subtype (*ST*):
* If *ST* is ``00``, this is a basic acknowledgement whose role can be
interpreted differently depending on the flow. It is not expected to be
extended, i.e. *EX* should be ``0``.
* If *ST* is ``01``, this is an overwrite confirmation; see
:ref:`seven-confirm-overwrite` for more details on the related flow.
* If *ST* is ``02``, this is an extended acknowledgement (EACK), providing
additional information, i.e. the packet is extended and *EX* should be
set to ``1``.
**This is only used in the flow to get device information**; see
:ref:`seven-get-device-information` and :ref:`seven-command-01`
for more information.
* If *ST* is ``03``, this means the acknowledge packet also marks the end
of the communication.
The payload for an extended acknowledgement is the following:
If *ST* is set to ``02``, the packet data (*D*) is expected to be 164 (0xA4)
bytes long, and structured the following way:
If *ST* is set to ``02``, the packet data (*D*) is expected to be one of:
* 164 (0xA4) bytes long for fx-9860G and compatible models.
* 188 (0xBC) bytes long for the fx-CG family of models.
The format of the 164 bytes long payload is the following:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 0 (0x00)
- 8 B
- Hardware Identifier (*HWID*)
- Hardware identifier for the calculator.
- See :ref:`seven-hardware-identifiers` for known hardware identifiers.
* - 8 (0x08)
- 16 B
- Processor Identifier (*CPUID*)
- String describing the processor used by the calculator.
Is known to be incorrect, it is recommended not to use this field.
- ``RENESAS SH735501``
* - 24 (0x18)
- 8 B
- Preprogrammed ROM capacity
- Capacity of the preprogrammed ROM, in KB
- 8-char **ASCII-DEC** value, e.g. ``00000000``.
* - 32 (0x20)
- 8 B
- Flash ROM capacity
- Capacity of the flash ROM, in KB
- 8-char **ASCII-DEC** value, e.g. ``00004096``.
* - 40 (0x28)
- 8 B
- RAM capacity
- Capacity of the RAM, in KB
- 8-char **ASCII-DEC** value, e.g. ``00000512``.
* - 48 (0x30)
- 16 B
- Preprogrammed ROM version
- Version of the preprogrammed ROM, as an OS version
- ``xx.xx.xxxx`` + ``\xFF\xFF\xFF\xFF\xFF\xFF``
* - 64 (0x40)
- 16 B
- Boot code version
- Version of the bootcode, as an OS version
- ``xx.xx.xxxx`` + ``\xFF\xFF\xFF\xFF\xFF\xFF``
* - 80 (0x50)
- 8 B
- Boot code offset
- Offset of the boot code on the ROM, as a 32-bit address.
- 8-char :ref:`seven-ascii-hex` value.
* - 88 (0x58)
- 8 B
- Boot code size
- Size of the boot code, in KB.
- 8-char :ref:`seven-ascii-hex` value.
* - 96 (0x60)
- 16 B
- OS code version
- Version of the OS, as an OS version
- ``xx.xx.xxxx`` + ``\xFF\xFF\xFF\xFF\xFF\xFF``
* - 112 (0x70)
- 8 B
- OS code offset
- Offset of the OS on the ROM, as a 32-bit address.
- 8-char :ref:`seven-ascii-hex` value.
* - 120 (0x78)
- 8 B
- OS code size
- Size of the OS on the ROM, in KB.
- 8-char :ref:`seven-ascii-hex` value.
* - 128 (0x80)
- 4 B
- Protocol version
- Version of the protocol in use.
- ``7.00``
* - 132 (0x84)
- 16 B
- Product Identifier
- Identifier of the calculator.
- 8-char alphabetical (upper and lower case) identifier.
* - 148 (0x94)
- 16 B
- User Name
- Name set by the user in SYSTEM
-
The format of the 164 bytes long payload is the following:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Field name
- Description
- Values
* - 0 (0x00)
- 148 B
- ...
- Equivalent to the 164 bytes long payload up to the Product Identifier,
included.
- See above.
* - 148 (0x94)
- 20 B
- User Name
- Set by the user in SYSTEM.
-
* - 168 (0xA8)
- 20 B
- Organisation Name
- Set by the user in SYSTEM.
-
.. _seven-nak-packet:
``0x15`` -- Negative acknowledgement (NAK) packet
-------------------------------------------------
This covers multiple packet types, depending on the subtype (*ST*):
* If *ST* is ``00``, a default error is returned.
* If *ST* is ``01``, the previously received packet is invalid and requires
resending from the other side. See :ref:`seven-report-invalid-checksum`
for more information on the usage of this error.
* If *ST* is ``02``, the operation requires an overwrite confirmation;
see :ref:`seven-confirm-overwrite` for more details on the related flow.
* If *ST* is ``03``, the active side is signalling an overwrite rejection from
the user to the passive side; see :ref:`seven-confirm-overwrite` for more
details on the related flow.
* If *ST* is ``04``, the error is a generic error that terminates the link.
* If *ST* is ``05``, the memory is full and the link is also terminated.
.. _seven-terminate-packet:
``0x18`` -- Terminate packet
----------------------------
This data packet is used by the active side to signal termination of
the link usage. The subtype (*ST*) represents the reason for termination:
* *ST* being set to ``00`` is the default case.
* If *ST* is set to ``01``, it means that the user has requested
termination from either the active or passive side.
* If *ST* is set to ``02``, it means that the termination is due to
timeouts, likely due to checking flows having failed; see
:ref:`seven-check-link` for more information.
* If *ST* is set to ``03``, it means that the termination is due to
an overwrite request having been denied by the user on the active side.
All termination packets should not be extended, i.e. *EX* should be ``0``.
See :ref:`seven-terminate-link` for more information on the link
termination flow.

View File

@ -0,0 +1,43 @@
Specific formats for Protocol 7.00
==================================
Protocol 7.00 is made so that the data is always printable.
There only notable exception for this is the basic packet type, i.e. the first
byte of all basic packets, which in most if not all cases is lesser
than 0x20.
In all other places, the protocol takes specific dispositions to make the
packets printable, described in the following sections.
.. _seven-ascii-hex:
ASCII-HEX
~~~~~~~~~
Numbers are represented with ASCII-HEX, i.e. by providing the ASCII-encoded
version of their hexadecimal representation. For example, 3886 (0xF2E) is
represented as ``0F2E`` (0x30, 0x46, 0x32, 0x45) on four bytes, or
``00000F2E`` (0x30, 0x30, 0x30, 0x30, 0x30, 0x46, 0x32, 0x45) on eight.
.. _seven-5c-padding:
0x5C padding
~~~~~~~~~~~~
Raw data may be escaped using "0x5C padding", i.e.:
* Characters in the 0x00-0x1F range have 0x20 added to their value, and
are prefixed by ``\`` (0x5C). For example, 0x06 becomes ``\&`` (0x5C,
0x26).
* Character ``\`` (0x5C) itself is prefixed by another ``\`` (0x5C),
but does not have a bump, i.e. ``\`` (0x5C) becomes ``\\`` (0x5C).
When decode such padded data, if a ``\`` (0x5C) is encountered, there is
necessarily a character afterwards. The obtained character for the group of
two characters depends on the second character:
* If the second character is ``\`` (0x5C), then the result must be
``\`` (0x5C).
* Otherwise, the resulting byte is the ASCII code of the second character,
to which we substract 0x20. For example, if ``\&`` (0x5C, 0x26) is
encountered, the final byte must be 0x26 minus 0x20, i.e. 0x06.

View File

@ -0,0 +1,280 @@
Protocol 7.00 use cases
=======================
This document inventories use cases of Protocol 7.00 that are exploited by
the library and command-line utilities.
.. _seven-send-file-to-storage:
Send a file to storage
----------------------
In order to send a file to storage, the flow is the following:
.. mermaid::
sequenceDiagram
Participant user as User
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '45'
alt Overwrite confirmation is required
passive->>active: Overwrite confirmation requested
active->>user: Request user confirmation
user->>active: Confirm or reject
alt User confirms overwrite
active->>passive: Confirm overwrite
else
active->>passive: Reject overwrite
end
end
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
Note over active,passive: Data transfer flow from<br />active side to passive
end
For more information, consult the following sections:
* Command :ref:`seven-command-45`;
* :ref:`seven-confirm-overwrite`;
* :ref:`seven-transmit-data`.
This flow is used by ``p7 send``; see the :ref:`p7-send` for more information.
.. _seven-request-file-from-storage:
Request a file from storage
---------------------------
In order to request a file to storage, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Original Active Side
Participant passive as Original Passive Side
active->>passive: Command '44'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
active->>passive: Roleswap
passive->>active: Command '45'
active->>passive: Acknowledge
Note over active,passive: Data transfer flow from<br />original passive side to active
passive->>active: Roleswap
end
For more information, consult the following sections:
* Command :ref:`seven-command-44`;
* Command :ref:`seven-command-45`;
* :ref:`seven-request-transfer`;
* :ref:`seven-transmit-data`.
This flow is used by ``p7 get``; see the :ref:`p7-get` for more information.
.. _seven-copy-file-on-storage:
Copy a file to another on storage
---------------------------------
In order to copy a file on storage, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '48'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
end
For more information, consult the following sections:
* Command :ref:`seven-command-48`.
This flow is used by ``p7 copy``; see the :ref:`p7-copy` for more information.
.. _seven-delete-file-on-storage:
Delete a file from storage
--------------------------
In order to delete a file on storage, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '46'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
end
For more information, consult the following sections:
* Command :ref:`seven-command-46`.
This flow is used by ``p7 delete``; see the :ref:`p7-delete`
for more information.
.. _seven-list-files-on-storage:
List files on storage
---------------------
In order to list files on storage, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Original Active Side
Participant passive as Original Passive Side
active->>passive: Command '4D'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
active->>passive: Roleswap
loop All file information has not been sent
passive->>active: Command '4E'
active->>passive: Acknowledge
end
passive->>active: Roleswap
end
For more information, consult the following sections:
* Command :ref:`seven-command-4D`;
* Command :ref:`seven-command-4E`;
* :ref:`seven-request-transfer`.
This flow is used by ``p7 list``; see the :ref:`p7-list` for more information.
.. _seven-reset-storage:
Reset a storage device
----------------------
In order to reset a storage device, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '4A'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
end
For more information, consult the following sections:
* Command :ref:`seven-command-4A`.
This flow is used by ``p7 reset``; see the :ref:`p7-reset`
for more information.
.. _seven-optimize-storage:
Optimize a storage device
-------------------------
In order to optimize a storage device, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '51'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
end
For more information, consult the following sections:
* Command :ref:`seven-command-51`.
This flow is used by ``p7 optimize``; see the :ref:`p7-optimize` for more
information.
.. _seven-upload-and-run:
Upload and run an executable program
------------------------------------
.. warning::
This is a dangerous flow, and is only documented here for completeness.
Programs suitable for this command, nicknamed "Update.exe", are crafted
in a very specific way, and you should not attempt to make your own.
In order to upload and run an executable program, the flow is the following:
.. mermaid::
sequenceDiagram
Participant active as Active Side
Participant passive as Passive Side
active->>passive: Command '56'
alt Error has occurred
passive->>active: Error
else
passive->>active: Acknowledge
Note over active,passive: Data transfer from active to passive
end
If successful, the last acknowledgement from the data transfer will have
the special subtype ``03``, which means that the communication is terminated
as control of the device is handed over to the program.
This use case is employed for updating the calculator, by uploading and running
special programs whose role is to open a USB or serial link, and receive and
flash the new OS at the command of the host. This has also been employed by
the community to do the same, such as with fxRemote_.
For more information, consult the following sections:
* Command :ref:`seven-command-56`;
* :ref:`seven-transmit-data`.
.. _fxRemote: https://tiplanet.org/forum/archives_voir.php?id=4484

View File

@ -0,0 +1,25 @@
.. _protocol-ums:
USB Mass Storage (UMS) and proprietary extensions for fx-CG calculators
=======================================================================
In order to behave like any other USB-attached storage with their "USB Key"
communications mode, fx-CG calculators implement `USB Mass Storage`_ (UMS)
with `Bulk-Only Transport`_.
The following sections describe CASIO's usage of UMS in this context.
.. note::
Extensions to the SCSI protocol embedded within CASIO's UMS implementation
still support a stream interface for communicating using Protocol 7.00
or Protocol 7.00 Screenstreaming over UMS; see
:ref:`ums-custom-commands` for more information.
.. toctree::
:maxdepth: 1
ums/custom-commands
.. _USB Mass Storage: https://en.wikipedia.org/wiki/USB_mass_storage_device_class
.. _Bulk-Only Transport: https://www.usb.org/sites/default/files/usbmassbulk_10.pdf

View File

@ -0,0 +1,133 @@
.. _ums-custom-commands:
Custom SCSI commands for CASIO's USB Mass Storage implementation
================================================================
CASIO makes use of the ``C0h`` to ``FFh`` `vendor-specific range`_ to
implement its own SCSI commands.
.. _ums-command-c0:
``0xC0`` -- Poll status
-----------------------
This command is a 16-byte command that is run to poll the device's status.
The command format is the following:
.. list-table::
* - Offset
- Size
- Description
- Value
* - 0 (0x00)
- 1 B
- Command code.
- ``0xC0``
* - 1 (0x01)
- 15 B
- Reserved.
- Must be set to ``0x00``.
The command prompts the device to answer with a 16-byte device status,
with the following format:
.. list-table::
:header-rows: 1
* - Offset
- Size
- Description
- Value
* - 0 (0x00)
- 1 B
- Unknown.
- Set to ``0xD0``.
* - 1 (0x01)
- 5 B
- Reserved.
- Set to ``0x00``.
* - 6 (0x06)
- 2 B
- Amount of available bytes to be requested.
- Big endian 16-bit integer.
* - 8 (0x08)
- 2 B
- Reserved.
- Set to ``0x00``.
* - 10 (0x0A)
- 2 B
- Activity status.
- Big endian 16-bit integer.
* - 12 (0x0C)
- 4 B
- Reserved.
- Set to ``0x00``.
.. _ums-command-c1:
``0xC1`` -- Request available data
----------------------------------
This command is a 16-byte long command that is run to read available data.
The command format is the following:
.. list-table::
* - Offset
- Size
- Description
- Value
* - 0 (0x00)
- 1 B
- Command code.
- ``0xC1``
* - 1 (0x01)
- 5 B
- Reserved.
- Set to ``0x00``.
* - 6 (0x06)
- 2 B
- Requested bytes count.
- Big endian 16-bit integer.
* - 8 (0x08)
- 8 B
- Reserved.
- Set to ``0x00``.
The command prompts the device to answer with the requested data.
.. _ums-command-c2:
``0xC2`` -- Send data
---------------------
This command is a 16-byte long command that is run to write data to the
calculator. The command format is the following:
.. list-table::
* - Offset
- Size
- Description
- Value
* - 0 (0x00)
- 1 B
- Command code.
- ``0xC2``
* - 1 (0x01)
- 5 B
- Reserved.
- Set to ``0x00``.
* - 6 (0x06)
- 2 B
- Bytes count.
- Big endian 16-bit integer.
* - 8 (0x08)
- 8 B
- Reserved.
- Set to ``0x00``.
The command should be accompanied with the data to send.
.. _Vendor-specific range: https://en.wikipedia.org/wiki/SCSI_command#SCSI_command_lengths

View File

@ -0,0 +1,51 @@
.. _usb-detection:
USB detection for CASIO calculators
===================================
When looking for CASIO calculators, the following vendor and product
identifiers (VID/PID) can be used:
.. list-table::
:header-rows: 1
* - Vendor ID
- Product ID
- Interface class
- Description
* - ``07cf``
- ``6101``
- ``255`` (Vendor-Specific)
- Graph 35+/75/85/95, fx-9860G Slim, Classpad 300
* - ``07cf``
- ``6102``
- ``8`` (Mass Storage)
- Classpad 330+, fx-CG20, fx-CP400, fx-CP400+E
The interface class ``255`` is used when the device presents protocol 7.00
directly, while the interface class ``8`` is used in "USB key" mode, i.e.
when the file system and main memory are presented using SCSI.
.. note::
For reference, the following USB serial cables have also be encountered
in the wild:
.. list-table::
:header-rows: 1
* - Vendor ID
- Product ID
- Description
* - ``0711``
- ``0230``
- SB-88 serial cable (official CASIO cable).
* - ``0bda``
- ``5606``
- Util-Pocket (defunct alternative vendor) serial cable.
Uses an USB serial converter from FTDI_.
Note however that these should be used through the system's serial
bus interface rather than directly.
.. _FTDI: https://ftdichip.com/

37
include/cahute.h Normal file
View File

@ -0,0 +1,37 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef CAHUTE_H
#define CAHUTE_H 1
#include <cahute/cdefs.h>
#include <cahute/detection.h>
#include <cahute/error.h>
#include <cahute/link.h>
#include <cahute/logging.h>
#include <cahute/picture.h>
#endif

332
include/cahute/cdefs.h Normal file
View File

@ -0,0 +1,332 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#if defined(OF)
#elif defined(__STDC__) && __STDC__
# define OF(CAHUTE__ARGS) CAHUTE__ARGS
#else
# define OF(CAHUTE__ARGS) ()
#endif
#ifndef CAHUTE_CDEFS_H
# define CAHUTE_CDEFS_H 1
# include <cahute/config.h>
/* C++ declaration and namespace management. */
# ifdef __cplusplus
# define CAHUTE_BEGIN_NAMESPACE namespace "cahute" {
# define CAHUTE_BEGIN_DECLS extern "C" {
# define CAHUTE_END_DECLS }
# define CAHUTE_END_NAMESPACE }
# else
# define CAHUTE_BEGIN_NAMESPACE
# define CAHUTE_BEGIN_DECLS
# define CAHUTE_END_DECLS
# define CAHUTE_END_NAMESPACE
# endif
CAHUTE_BEGIN_NAMESPACE
/* Macro to check the library version. */
# define CAHUTE_PREREQ(CAHUTE__MAJ, CAHUTE__MIN) \
((CAHUTE__MAJ) > (CAHUTE_MAJOR) \
|| ((CAHUTE__MAJ) == (CAHUTE_MAJOR) && (CAHUTE__MIN) >= (CAHUTE_MINOR)) \
)
/* ---
* Compiler and language specific macros.
* --- */
/* Macro to check if we have at least a specific version of GCC. */
# if defined(CAHUTE_GNUC_PREREQ)
# elif defined(__GNUC__) && defined(__GNUC_MINOR__)
# define CAHUTE_GNUC_PREREQ(CAHUTE__MAJ, CAHUTE__MIN) \
((__GNUC__ << 16) + __GNUC_MINOR__ \
>= ((CAHUTE__MAJ) << 16) + (CAHUTE__MIN))
# else
# define CAHUTE_GNUC_PREREQ(CAHUTE__MAJ, CAHUTE__MIN) 0
# endif
/* Macro to check if we have at least a specific version of MSC. */
# if defined(CAHUTE_MSC_PREREQ) || defined(_MSC_VER)
# define CAHUTE_MSC_PREREQ(CAHUTE__MAJ, CAHUTE__MIN) \
(_MSC_VER >= (CAHUTE__MAJ) * 1000 + (CAHUTE__MIN))
# else
# define CAHUTE_MSC_PREREQ(CAHUTE__MAJ, CAHUTE__MIN) 0
# endif
/* Export whether the function is deprecated. */
# if CAHUTE_GNUC_PREREQ(3, 0)
# define CAHUTE_DEPRECATED __attribute__((deprecated))
# else
# define CAHUTE_DEPRECATED
# endif
/* Warn if the result is unused (Warn Unused Result). */
# if CAHUTE_GNUC_PREREQ(4, 0)
# define CAHUTE_WUR __attribute__((warn_unused_result))
# elif CAHUTE_MSC_PREREQ(17, 0)
# include <sal.h>
# define CAHUTE_WUR _Check_return_
# else
# define CAHUTE_WUR
# endif
/* Export the function to be used in an extern context. */
# define CAHUTE_EXTERN(TYPE) extern TYPE
/* Make a function local. */
# define CAHUTE_LOCAL(TYPE) static TYPE
/* Make a function local and inline. */
# define CAHUTE_INLINE(TYPE) static inline TYPE
/* Make some data local. */
# define CAHUTE_LOCAL_DATA(TYPE) static TYPE
/* Request non-null parameters.
* CAHUTE_NNP is to be used inline, CAHUTE_NNP_ATTR is to be used after the
* function declaration. Both should be defined for portability.
*
* For example:
*
* int my_function(char const CAHUTE_NNPTR(my_string)) CAHUTE_NONNULL((1));
*
* Which can give any of, depending on the environment:
*
* int my_function(char const my_string[static 1]);
* int my_function(char const *my_string) __attribute__((nonnull (1)));
* int my_function(char const *my_string);
*/
/* TODO: Define these in some cases. */
# define CAHUTE_NNPTR(NAME) *NAME
# define CAHUTE_NONNULL(INDEXES)
/* Declare a structure. */
# define CAHUTE_DECLARE_TYPE(NAME) \
struct NAME; \
typedef struct NAME NAME;
/* ---
* Integer definitions.
* --- */
# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
# include <inttypes.h>
# include <stdint.h>
/* `stdint.h` and `inttypes.h` are standard C99 headers.
* `stdint.h` provides the `uintN_t` types, and
* `inttypes.h` provides the `PRI[uxX]N` macros. */
typedef uint8_t cahute_u8;
typedef uint16_t cahute_u16;
typedef uint32_t cahute_u32;
# define CAHUTE_PRIu8 PRIu8
# define CAHUTE_PRIx8 PRIx8
# define CAHUTE_PRIX8 PRIX8
# define CAHUTE_PRIu16 PRIu16
# define CAHUTE_PRIx16 PRIx16
# define CAHUTE_PRIX16 PRIX16
# define CAHUTE_PRIu32 PRIu32
# define CAHUTE_PRIx32 PRIx32
# define CAHUTE_PRIX32 PRIX32
# else /* C89 */
# include <limits.h>
/* Here, we ought to do some C89 hacking.
* We'll use the `limits.h` definitions to try and guess which one of the
* default types are the 8-bit, 16-bit and 32-bit integer. */
# define CAHUTE_P8 "hh"
typedef unsigned char cahute_u8;
/* 16-bit integer. */
# if (USHRT_MAX > 0xffffUL)
# error "No 16-bit type, exiting!"
# endif
# define CAHUTE_P16 "h"
typedef unsigned short cahute_u16;
/* 32-bit integer. */
# if (UINT_MAX == 0xffffffffUL)
# define CAHUTE_P32 ""
typedef unsigned int cahute_u32;
# elif (ULONG_MAX == 0xffffffffUL)
# define CAHUTE_P32 "l"
typedef unsigned long cahute_u32;
# else
/* There is nothing between `char` and `short`, and `char` is always
* byte-wide;
*
* `long long` is not defined in C89, and even if it can be used as a
* compiler extension for C89, it is supposed to be 64-bit or more.
* So basically we're running out of options here. */
# error "No 32-bit type, exiting!"
# endif
# define CAHUTE_PRIu8 CAHUTE_P8 "u"
# define CAHUTE_PRIx8 CAHUTE_P8 "x"
# define CAHUTE_PRIX8 CAHUTE_P8 "X"
# define CAHUTE_PRIu16 CAHUTE_P16 "u"
# define CAHUTE_PRIx16 CAHUTE_P16 "x"
# define CAHUTE_PRIX16 CAHUTE_P16 "X"
# define CAHUTE_PRIu32 CAHUTE_P32 "u"
# define CAHUTE_PRIx32 CAHUTE_P32 "x"
# define CAHUTE_PRIX32 CAHUTE_P32 "X"
# endif
/* printf definition for `size_t`. */
# if defined(_WIN64)
# define CAHUTE_PRIuSIZE "l64u"
# define CAHUTE_PRIxSIZE "l64x"
# define CAHUTE_PRIXSIZE "l64X"
# elif defined(_WIN32)
# define CAHUTE_PRIuSIZE "u"
# define CAHUTE_PRIxSIZE "x"
# define CAHUTE_PRIXSIZE "X"
# else
# define CAHUTE_PRIuSIZE "zu"
# define CAHUTE_PRIxSIZE "zx"
# define CAHUTE_PRIXSIZE "zX"
# endif
/* ---
* Endianess management.
* --- */
CAHUTE_BEGIN_DECLS
CAHUTE_EXTERN(cahute_u16) cahute_be16toh OF((cahute_u16 cahute__x));
CAHUTE_EXTERN(cahute_u16) cahute_le16toh OF((cahute_u16 cahute__x));
CAHUTE_EXTERN(cahute_u32) cahute_be32toh OF((cahute_u32 cahute__x));
CAHUTE_EXTERN(cahute_u32) cahute_le32toh OF((cahute_u32 cahute__x));
CAHUTE_EXTERN(cahute_u16) cahute_htobe16 OF((cahute_u16 cahute__x));
CAHUTE_EXTERN(cahute_u16) cahute_htole16 OF((cahute_u16 cahute__x));
CAHUTE_EXTERN(cahute_u32) cahute_htobe32 OF((cahute_u32 cahute__x));
CAHUTE_EXTERN(cahute_u32) cahute_htole32 OF((cahute_u32 cahute__x));
CAHUTE_END_DECLS
/* Try to get native macros. */
# if defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# define cahute_macro_be16toh(CAHUTE__X) OSSwapBigToHostInt16(CAHUTE__X)
# define cahute_macro_le16toh(CAHUTE__X) OSSwapLittleToHostInt16(CAHUTE__X)
# define cahute_macro_be32toh(CAHUTE__X) OSSwapBigToHostInt32(CAHUTE__X)
# define cahute_macro_le32toh(CAHUTE__X) OSSwapLittleToHostInt32(CAHUTE__X)
# define cahute_macro_htobe16(CAHUTE__X) OSSwapHostToBigInt16(CAHUTE__X)
# define cahute_macro_htole16(CAHUTE__X) OSSwapHostToLittleInt16(CAHUTE__X)
# define cahute_macro_htobe32(CAHUTE__X) OSSwapHostToBigInt32(CAHUTE__X)
# define cahute_macro_htole32(CAHUTE__X) OSSwapHostToLittleInt32(CAHUTE__X)
# elif defined(__OpenBSD__)
# include <sys/endian.h>
# define cahute_macro_be16toh(CAHUTE__X) be16toh(CAHUTE__X)
# define cahute_macro_le16toh(CAHUTE__X) le16toh(CAHUTE__X)
# define cahute_macro_be32toh(CAHUTE__X) be32toh(CAHUTE__X)
# define cahute_macro_le32toh(CAHUTE__X) le32toh(CAHUTE__X)
# define cahute_macro_htobe16(CAHUTE__X) htobe16(CAHUTE__X)
# define cahute_macro_htole16(CAHUTE__X) htole16(CAHUTE__X)
# define cahute_macro_htobe32(CAHUTE__X) htobe32(CAHUTE__X)
# define cahute_macro_htole32(CAHUTE__X) htole32(CAHUTE__X)
# elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) \
|| defined(__WINDOWS__)
# include <sys/param.h>
# include <winsock2.h>
# if BYTE_ORDER == LITTLE_ENDIAN
# define cahute_macro_be16toh(CAHUTE__X) ntohs(CAHUTE__X)
# define cahute_macro_le16toh(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_be32toh(CAHUTE__X) ntohl(CAHUTE__X)
# define cahute_macro_le32toh(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_htobe16(CAHUTE__X) htons(CAHUTE__X)
# define cahute_macro_htole16(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_htobe32(CAHUTE__X) htonl(CAHUTE__X)
# define cahute_macro_htole32(CAHUTE__X) (CAHUTE__X)
# else
# define cahute_macro_be16toh(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_le16toh(CAHUTE__X) ntohs(CAHUTE__X)
# define cahute_macro_be32toh(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_le32toh(CAHUTE__X) ntohl(CAHUTE__X)
# define cahute_macro_htobe16(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_htole16(CAHUTE__X) htons(CAHUTE__X)
# define cahute_macro_htobe32(CAHUTE__X) (CAHUTE__X)
# define cahute_macro_htole32(CAHUTE__X) htonl(CAHUTE__X)
# endif
# elif defined(__GLIBC__) && defined(__USE_MISC)
# include <endian.h>
# define cahute_macro_be16toh(CAHUTE__X) be16toh(CAHUTE__X)
# define cahute_macro_le16toh(CAHUTE__X) le16toh(CAHUTE__X)
# define cahute_macro_be32toh(CAHUTE__X) be32toh(CAHUTE__X)
# define cahute_macro_le32toh(CAHUTE__X) le32toh(CAHUTE__X)
# define cahute_macro_htobe16(CAHUTE__X) htobe16(CAHUTE__X)
# define cahute_macro_htole16(CAHUTE__X) htole16(CAHUTE__X)
# define cahute_macro_htobe32(CAHUTE__X) htobe32(CAHUTE__X)
# define cahute_macro_htole32(CAHUTE__X) htole32(CAHUTE__X)
# endif
/* CAHUTE_NO_ENDIAN may be defined by cdefs.c to be able to define the
* functions prototyped above. */
# ifndef CAHUTE_NO_ENDIAN
# ifdef cahute_macro_be16toh
# define cahute_be16toh(CAHUTE__X) cahute_macro_be16toh(CAHUTE__X)
# endif
# ifdef cahute_macro_le16toh
# define cahute_le16toh(CAHUTE__X) cahute_macro_le16toh(CAHUTE__X)
# endif
# ifdef cahute_macro_be32toh
# define cahute_be32toh(CAHUTE__X) cahute_macro_be32toh(CAHUTE__X)
# endif
# ifdef cahute_macro_le32toh
# define cahute_le32toh(CAHUTE__X) cahute_macro_le32toh(CAHUTE__X)
# endif
# ifdef cahute_macro_htobe16
# define cahute_htobe16(CAHUTE__X) cahute_macro_htobe16(CAHUTE__X)
# endif
# ifdef cahute_macro_htole16
# define cahute_htole16(CAHUTE__X) cahute_macro_htole16(CAHUTE__X)
# endif
# ifdef cahute_macro_htobe32
# define cahute_htobe32(CAHUTE__X) cahute_macro_htobe32(CAHUTE__X)
# endif
# ifdef cahute_macro_htole32
# define cahute_htole32(CAHUTE__X) cahute_macro_htole32(CAHUTE__X)
# endif
# endif
CAHUTE_END_NAMESPACE
#endif /* CAHUTE_CDEFS_H */

View File

@ -0,0 +1,47 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef CAHUTE_CONFIG_H
# define CAHUTE_CONFIG_H 1
# define CAHUTE_LOGLEVEL_INFO 10
# define CAHUTE_LOGLEVEL_WARNING 20
# define CAHUTE_LOGLEVEL_ERROR 30
# define CAHUTE_LOGLEVEL_FATAL 40
# define CAHUTE_LOGLEVEL_NONE 50
# define CAHUTE_VERSION "${PROJECT_VERSION}"
# define CAHUTE_VERNUM ${PROJECT_VERSION_HEX}
# define CAHUTE_MAJOR ${PROJECT_VERSION_MAJOR}
# define CAHUTE_MINOR ${PROJECT_VERSION_MINOR}
# define CAHUTE_DEFAULT_LOGLEVEL CAHUTE_LOGLEVEL_${DEFAULT_LOGLEVEL}
# define CAHUTE_URL "${PROJECT_HOMEPAGE_URL}"
# define CAHUTE_ISSUES_URL "${PROJECT_ISSUES_URL}"
#endif /* CAHUTE_CONFIG_H */

View File

@ -0,0 +1,80 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef CAHUTE_DETECTION_H
#define CAHUTE_DETECTION_H 1
#include "cdefs.h"
CAHUTE_BEGIN_NAMESPACE
CAHUTE_DECLARE_TYPE(cahute_serial_detection_entry)
CAHUTE_DECLARE_TYPE(cahute_usb_detection_entry)
struct cahute_serial_detection_entry {
char const *cahute_serial_detection_entry_name;
};
#define CAHUTE_USB_DETECTION_ENTRY_TYPE_SEVEN 1
#define CAHUTE_USB_DETECTION_ENTRY_TYPE_SCSI 2
struct cahute_usb_detection_entry {
int cahute_usb_detection_entry_bus;
int cahute_usb_detection_entry_address;
int cahute_usb_detection_entry_type;
};
typedef int(cahute_detect_serial_entry_func)(
void *cookie,
cahute_serial_detection_entry const *entry
);
typedef int(cahute_detect_usb_entry_func)(
void *cookie,
cahute_usb_detection_entry const *entry
);
/* ---
* List available devices.
* --- */
CAHUTE_BEGIN_DECLS
CAHUTE_EXTERN(int)
cahute_detect_serial
OF((cahute_detect_serial_entry_func CAHUTE_NNPTR(cahute__func),
void *cahute__cookie)) CAHUTE_NONNULL(1);
CAHUTE_EXTERN(int)
cahute_detect_usb
OF((cahute_detect_usb_entry_func CAHUTE_NNPTR(cahute__func),
void *cahute__cookie)) CAHUTE_NONNULL(1);
CAHUTE_END_DECLS
CAHUTE_END_NAMESPACE
#endif /* CAHUTE_DETECTION_H */

52
include/cahute/error.h Normal file
View File

@ -0,0 +1,52 @@
/* ****************************************************************************
* Copyright (C) 2024 Thomas Touhey <thomas@touhey.fr>
*
* This software is governed by the CeCILL 2.1 license under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL 2.1 license
* as circulated by CEA, CNRS and INRIA at the following
* URL: https://cecill.info
*
* As a counterpart to the access to the source code and rights to copy, modify
* and redistribute granted by the license, users are provided only with a
* limited warranty and the software's author, the holder of the economic
* rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with
* loading, using, modifying and/or developing or reproducing the software by
* the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it
* is reserved for developers and experienced professionals having in-depth
* computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling
* the security of their systems and/or data to be ensured and, more generally,
* to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL 2.1 license and that you accept its terms.
* ************************************************************************* */
#ifndef CAHUTE_ERROR_H
#define CAHUTE_ERROR_H 1
CAHUTE_BEGIN_NAMESPACE
#define CAHUTE_OK 0x0000 /* No error has occurred. */
#define CAHUTE_ERROR_UNKNOWN 0x0001 /* An unknown error has occurred. */
#define CAHUTE_ERROR_IMPL 0x0002 /* A feature was unimplemented. */
#define CAHUTE_ERROR_ALLOC 0x0003 /* A memory allocation has failed. */
#define CAHUTE_ERROR_PRIV 0x0004 /* Insufficient privileges were found. */
#define CAHUTE_ERROR_INT 0x0005 /* Interrupted by a callback. */
#define CAHUTE_ERROR_SIZE 0x0006 /* Some received data was too big. */
#define CAHUTE_ERROR_NOT_FOUND 0x0101 /* Device could not be found. */
#define CAHUTE_ERROR_TOO_MANY 0x0102 /* Too Many Devices found. */
#define CAHUTE_ERROR_INCOMPAT 0x0103 /* Found device is incompatible. */
#define CAHUTE_ERROR_GONE 0x0104 /* Device is gone or I/O has failed. */
#define CAHUTE_ERROR_TIMEOUT 0x0105 /* A timeout has occurred. */
#define CAHUTE_ERROR_CORRUPT 0x0106 /* Corrupted packet (invalid checksum) */
#define CAHUTE_ERROR_IRRECOV 0x0107 /* Irrecoverable link */
#define CAHUTE_ERROR_NOOW 0x0201 /* File was not overwritten. */
CAHUTE_END_NAMESPACE
#endif /* CAHUTE_ERROR_H */

Some files were not shown because too many files have changed in this diff Show More