mirror of
https://github.com/erichelgeson/BlueSCSI.git
synced 2024-10-31 18:04:27 +00:00
Compare commits
184 Commits
v1.1-20220
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
bf5823468a | ||
|
2baa0155e1 | ||
|
3a9db99723 | ||
|
70eda86484 | ||
|
9d4207ea14 | ||
|
1d49003fe1 | ||
|
0a30396c3b | ||
|
ba06827814 | ||
|
a45af3edec | ||
|
0be37ccde0 | ||
|
e236b5b054 | ||
|
47179c5fd0 | ||
|
59c858332a | ||
|
dddb7bfc1c | ||
|
8b79df00ba | ||
|
7c9428131b | ||
|
e35bae560c | ||
|
13d7b985b7 | ||
|
27b7427301 | ||
|
98162fcd84 | ||
|
2be810e2ca | ||
|
4f7c0fe2c2 | ||
|
458617a827 | ||
|
810eae9be3 | ||
|
d67fa80cf0 | ||
|
9e746ba145 | ||
|
e0d115f33c | ||
|
27d13192b0 | ||
|
43335afbdb | ||
|
ad4737f384 | ||
|
927f8101fa | ||
|
142117c83a | ||
|
c5eff4f5fd | ||
|
c901efb929 | ||
|
2d0b466296 | ||
|
075e21d981 | ||
|
71b1d3ca31 | ||
|
5bad53008a | ||
|
c2c4b83925 | ||
|
5506a90042 | ||
|
b117ebb39d | ||
|
139a510ee7 | ||
|
7ee2c44c6a | ||
|
f4d7cdfa92 | ||
|
a8903d42f2 | ||
|
0ae77a7b48 | ||
|
726d72c09b | ||
|
dc0bce859c | ||
|
dba1860856 | ||
|
6ec75300c4 | ||
|
5bb2dee71f | ||
|
bb4b688cad | ||
|
44b6270ef8 | ||
|
30cf64ff83 | ||
|
d3ef149aa3 | ||
|
e930ecfcde | ||
|
847456203f | ||
|
fb3a266c86 | ||
|
330c3edd91 | ||
|
57ff0cd3b4 | ||
|
452930940c | ||
|
7c6f9aef75 | ||
|
a7af02057f | ||
|
19f6b428a1 | ||
|
b0a47bc376 | ||
|
dc9552410f | ||
|
67d3909dd9 | ||
|
3f279230d9 | ||
|
331cd96ac5 | ||
|
3952181d4f | ||
|
c12aa478d5 | ||
|
9831c38237 | ||
|
94a5e711e3 | ||
|
6b2152d1f7 | ||
|
a06e840e39 | ||
|
32453b9886 | ||
|
3a3d68cf99 | ||
|
30b794e03d | ||
|
2611ec885b | ||
|
953309be30 | ||
|
b00dac7339 | ||
|
b53524e6c7 | ||
|
16c0cfd57a | ||
|
40e4590686 | ||
|
d4f86ddaed | ||
|
ff8990e5c5 | ||
|
173cc6126a | ||
|
2a95bc35ff | ||
|
219e3d0f23 | ||
|
2bd79bb487 | ||
|
53ad02384d | ||
|
e26e41dcbd | ||
|
2f8dd8a3fe | ||
|
5d80b8adbf | ||
|
e5d78139de | ||
|
0895f00094 | ||
|
58e880a9e1 | ||
|
54500e18b8 | ||
|
ca9089b405 | ||
|
f8cd61fbf9 | ||
|
a2a2d82d4b | ||
|
b74eecfa7a | ||
|
63a1fe9c41 | ||
|
423b054f5a | ||
|
d97ea88d17 | ||
|
45524964d6 | ||
|
28078212f5 | ||
|
52a0936fbc | ||
|
4dbba493d7 | ||
|
f29da7c88c | ||
|
ad87d86b7a | ||
|
d26bf0a15a | ||
|
f7075df6d8 | ||
|
3b7aa0d160 | ||
|
8e4b4ee512 | ||
|
557f34e5da | ||
|
11901d6a0d | ||
|
1cfaf00594 | ||
|
07961108c7 | ||
|
b01c2ffd7c | ||
|
d5405ab5f0 | ||
|
bfa59a1fa4 | ||
|
f69cff42e4 | ||
|
9b2b3f689f | ||
|
eeacf3e746 | ||
|
e52957315e | ||
|
2641e80fac | ||
|
437b2531ca | ||
|
dcbb7df80d | ||
|
1aecf323e1 | ||
|
d6ff7017c9 | ||
|
8167ddea6c | ||
|
2338d8f153 | ||
|
b16dc558dc | ||
|
a5c6edadc3 | ||
|
79d5b12110 | ||
|
d6fb46d387 | ||
|
c2c3397b72 | ||
|
906b8be9aa | ||
|
3c258451fa | ||
|
7cdddf7371 | ||
|
20cb5d8552 | ||
|
5c0724703b | ||
|
61ae8a59db | ||
|
19cc9ad169 | ||
|
14321e3ba6 | ||
|
b0799bc327 | ||
|
2127b14863 | ||
|
3ef2474ae8 | ||
|
9d1b58cdb6 | ||
|
1f03996977 | ||
|
73fc0ed2bf | ||
|
94a22bb45e | ||
|
c8d47218b1 | ||
|
853956e441 | ||
|
69e69cad3a | ||
|
bbd91936fb | ||
|
c04027c8ca | ||
|
b898d9684f | ||
|
38904285d4 | ||
|
8a27bcc585 | ||
|
c5447a4098 | ||
|
408e155a63 | ||
|
f7799fb225 | ||
|
845954edeb | ||
|
7b7f19413c | ||
|
961ae0b8b7 | ||
|
aa751dc3b8 | ||
|
fff520959e | ||
|
2f08b9fa73 | ||
|
24e1612fc7 | ||
|
a566c9bbb0 | ||
|
9a4e3dc886 | ||
|
27c424d2f9 | ||
|
3158391c86 | ||
|
ceb6da764b | ||
|
b4b0ce711f | ||
|
77698d0a68 | ||
|
d593c0817c | ||
|
adca34aeb8 | ||
|
693ab2a5c0 | ||
|
4d3ee87c56 | ||
|
ba1b3df680 | ||
|
22eb2c8d3e |
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@ -27,8 +27,5 @@ jobs:
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
|
||||
- name: Install library dependencies
|
||||
run: pio lib -g install 1
|
||||
|
||||
- name: Run PlatformIO
|
||||
run: pio ci --project-conf platformio.ini .
|
||||
run: pio run -e STM32F1 -e STM32F1-USB -e STM32F1-XCVR -e STM32F1-USB-96MHz -e STM32F1-USB-128MHz
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
.DS_Store
|
||||
.vscode/
|
||||
workspace.code-workspace
|
||||
dist/
|
||||
hw/*/*-backups/
|
||||
|
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
# Mounts
|
||||
|
||||
## Desktop
|
||||
## Desktop
|
||||
|
||||
Provides mouting holes for the BlueSCSI along with side and bottom holes for common mounting in computers.
|
||||
|
||||
@ -10,6 +10,8 @@ Please pre-tap any screw holes before mounting.
|
||||
|
||||
Print with supports for front overhangs. Tight fit to hold everything together.
|
||||
|
||||
Remix = a slightly modified version with light-pipes for the LEDs and some geometry fixes.
|
||||
|
||||
# License
|
||||
|
||||
BlueSCSI 3D files © 2021 by Eric Helgeson is licensed under Attribution-NonCommercial 4.0 International
|
||||
|
BIN
3D-Files/1.1/external-db25/BlueSCSI 1.1-a-DB25-Base_remix.jpg
Normal file
BIN
3D-Files/1.1/external-db25/BlueSCSI 1.1-a-DB25-Base_remix.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 KiB |
BIN
3D-Files/1.1/external-db25/BlueSCSI 1.1-a-DB25-Base_remix.stl
Normal file
BIN
3D-Files/1.1/external-db25/BlueSCSI 1.1-a-DB25-Base_remix.stl
Normal file
Binary file not shown.
BIN
3D-Files/1.1/external-db25/BlueSCSI 1.1-a-DB25-Lid_remix.stl
Normal file
BIN
3D-Files/1.1/external-db25/BlueSCSI 1.1-a-DB25-Lid_remix.stl
Normal file
Binary file not shown.
BIN
3D-Files/1.1/internal-50pin/bluescsi11a_kerosmacmodsv2.stl.zip
Normal file
BIN
3D-Files/1.1/internal-50pin/bluescsi11a_kerosmacmodsv2.stl.zip
Normal file
Binary file not shown.
@ -10,7 +10,7 @@ BlueSCSI & ArdSCSino are hardware that reproduces SCSI devices (hard disks) with
|
||||
* Enable/Disable Passive SCSI termination
|
||||
* An alternative power source if not able to be powered by the SCSI bus
|
||||
* Documentation
|
||||
* Open Hardware (KiCad & Grebers)
|
||||
* Open Hardware (KiCad & Gerbers)
|
||||
|
||||
`ArdSCSino-stm32` created by https://github.com/ztto/ArdSCSino-stm32[ztto] is the STM32 version of `ArdSCSino`
|
||||
|
||||
@ -63,7 +63,7 @@ https://github.com/erichelgeson/BlueSCSI/wiki/Troubleshooting
|
||||
|
||||
#### 1.1-a Desktop
|
||||
|
||||
* Binky! There are now resistors and pin outs for Power and Activity LED's
|
||||
* Blinky! There are now resistors and pin outs for Power and Activity LED's
|
||||
* Resistor nets flipped all the same way
|
||||
* 50 pin allows for right angle connector
|
||||
* SD Card facing out the back - gives some better options for mounting and access
|
||||
|
14
dist.sh
Normal file
14
dist.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
DATE=$(date +%Y%m%d)
|
||||
VERSION="v1.1-$DATE"
|
||||
mkdir -p dist
|
||||
rm -f dist/*.bin
|
||||
|
||||
for f in $(ls .pio/build/*/firmware.bin); do
|
||||
NAME="BlueSCSI-$VERSION-$(echo $f | cut -d\/ -f3).bin"
|
||||
cp $f dist/$NAME
|
||||
done
|
@ -3,25 +3,25 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
1.0-c was a small change to fix an issue with machines that provide low or no `TERMPOWER` over the SCSI bus. If you recived a Fully Assembled BlueSCSI after April 1st, 2021 then this was done for you. If you have a kit labled 1.0-b you can make these modifications to become a 1.0-c.
|
||||
1.0-c was a small change to fix an issue with machines that provide low or no `TERMPOWER` over the SCSI bus. If you received a fully-assembled BlueSCSI after April 1st, 2021 then this was done for you. If you have a kit labeled 1.0-b you can make these modifications to become a 1.0-c.
|
||||
|
||||
## Steps
|
||||
|
||||
### Disconnect `TERMPOWER` from 220 Pull Up Jupmer
|
||||
### Disconnect `TERMPOWER` from 220 Pull Up Jumper
|
||||
|
||||
Use a cutting tool to cut the small trace between the `TERMPOWR` and 220 Pull Up Jumper pictured below.
|
||||
|
||||
Make sure continiutiy from `TERMPOWER` to the diode still flows correctly but is disconnected from the jumper.
|
||||
Make sure continuity from `TERMPOWER` to the diode still flows correctly but is disconnected from the jumper.
|
||||
|
||||
image::images/1.0-b-bodge-1.jpg[]
|
||||
|
||||
### Connect 5V to 220 Pull Up Jupmer
|
||||
### Connect 5V to 220 Pull Up Jumper
|
||||
|
||||
Now we will provide pull up from the 5v provided by the BluePill.
|
||||
Now we will provide pull up from the 5V provided by the BluePill.
|
||||
|
||||
image::images/1.0-b-bodge-2.png[]
|
||||
|
||||
### Complete!
|
||||
|
||||
Thats it! 5v to the Pull Up resistors will now work when powered externally.
|
||||
That’s it! 5V to the Pull Up resistors will now work when powered externally.
|
||||
|
||||
|
@ -1,66 +1,5 @@
|
||||
# Assembly
|
||||
|
||||
## Before you start
|
||||
Instructions have moved to the wiki
|
||||
|
||||
Read through all instructions before starting.
|
||||
|
||||
## Videos
|
||||
|
||||
The community has created a few assembly videos:
|
||||
* [Stephen Arsenault - BlueSCSI Kit Assembly](https://www.youtube.com/watch?v=G6lwCfLQwd8) 2:52
|
||||
* [Joe's Computer Museum - A New Challenger - BlueSCSI - #MARCHintosh Review](https://www.youtube.com/watch?v=d_8VVmvvlNI) 26:52
|
||||
* [Geeky Bit - Tutorial - BlueSCSI Assembly](https://www.youtube.com/watch?v=-nF9FYFgVHM) 20:35
|
||||
|
||||
## Building
|
||||
|
||||
1. Solder on the surface mount SD Card holder. Use the [Drag Solder](https://youtu.be/Z_KL4fWOMug) technique.
|
||||
- Once on, check continuity of all connections. Fixing this after the BluePill is on will be difficult.
|
||||
- Ensure the SD card fits and you dont have too much solder on the sides or contacts
|
||||
2. Solder the diodes to the **bottom** of the board. Be sure they are flush as possible to the bottom.
|
||||
![Diode Orientation](images/diodes.png)
|
||||
3. Solder the header pins on to the BluePill. Then BluePill pill to the BlueSCSI PCB.
|
||||
- Correct orientation is having the USB connector of the BluePill should be over the SD Card holder.
|
||||
- **NOTE**: Consider adding headers to the board if you would like to have the BluePill removable.
|
||||
4. Solder on the resistor nets. Pin one is marked on the board and the resistors.
|
||||
- 220 RNETS (`RN1` & `RN3`) are closest to the BluePill
|
||||
- **NOTE** PIN one on each - they are opposite of eachtoher. See photo.
|
||||
- 330 RNETS (`RN2` & `RN4`) are furthest from the BluePill
|
||||
- **NOTE** PIN one on each - they are opposite of eachtoher. See photo.
|
||||
![Orientation RNETs and BluePill](images/orientation.png)
|
||||
5. Solder on the Termination Jumpers & place the jumpers on to enable Termiation
|
||||
6. Solder the 50 Pin headers on.
|
||||
1. Note Pin 1 and Box header on the silk screen.
|
||||
7. Optional - Solder on the berg connector. Only needed if your machine does not supply enough power via the SCSI bus.
|
||||
8. Optional - Solder on the debug headers near the SD card. Only nessissarry if re-programming.
|
||||
|
||||
### Fully Assembled
|
||||
![Fully Assembled](images/assembled.png)
|
||||
|
||||
### Version 1.0-b & 1.0-c BOM
|
||||
|
||||
#### Mouser Cart
|
||||
|
||||
https://www.mouser.com/ProjectManager/ProjectDetail.aspx?AccessID=c5cc83feff
|
||||
|
||||
#### Parts
|
||||
|
||||
STM32F103C8T6 (U1) aka BluePill board. Beware of fake/scams. Look for reviews. Amazon, ebay, etc.
|
||||
|
||||
* 2x 330 (RN2, RN4) Resistor nets https://www.mouser.com/ProductDetail/652-4610X-1LF-330
|
||||
|
||||
* 2x 220 (RN1, RN3) Resistor nets https://www.mouser.com/ProductDetail/652-4610X-1LF-220
|
||||
|
||||
* 2x Diodes https://www.mouser.com/ProductDetail/583-1N5818-T
|
||||
|
||||
* SD Card Holder (J1) https://www.mouser.com/ProductDetail/538-104031-0811
|
||||
|
||||
* Headers https://www.mouser.com/ProductDetail/872-920-0011-01
|
||||
|
||||
* 50 PIN SCSI header https://www.mouser.com/ProductDetail/517-30350-6002/
|
||||
- May substitute with 2 rows of headers from above.
|
||||
- **Note on Right angle connectors** - it appears Right angle headers are notched differently then the pin out. Be sure to note PIN 1/red stripe on the cable match up.
|
||||
|
||||
### Optional
|
||||
|
||||
Floppy/Berg Power header https://www.mouser.com/ProductDetail/571-1718254
|
||||
- Required if your machine does not supply power via TERM POWER. For example a Mac Plus or Mac with a Analog/PSU board that has not been recapped.
|
||||
https://github.com/erichelgeson/BlueSCSI/wiki/Assembly
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
# Build Your Own
|
||||
|
||||
These instructions will guide you thorugh purchasing all of the parts needed to assemble kits for yourselves and for friends! Please note the hardware is licensed non-comercial.
|
||||
These instructions will guide you through purchasing all of the parts needed to assemble kits for yourselves and for friends! Please note the hardware is licensed non-commercial.
|
||||
|
||||
If you'd like to buy the kits pre-packaged/pre-flashed or fully assembled we have a network of makers here to help you out at https://scsi.blue
|
||||
|
||||
@ -11,11 +11,11 @@ Updated for 1.0-c.
|
||||
|
||||
### PCB's
|
||||
|
||||
Grab the latest Gerbers. Gerber files tell the PCB manufactuer how to create the BlueSCSI boards.
|
||||
Grab the latest Gerbers. Gerber files tell the PCB manufacturer how to create the BlueSCSI boards.
|
||||
|
||||
https://github.com/erichelgeson/BlueSCSI/tree/main/hw/
|
||||
|
||||
Order the boards from https://jlcpcb.com/ or your manufactuer of choice.
|
||||
Order the boards from https://jlcpcb.com/ or your manufacturer of choice.
|
||||
|
||||
### Components
|
||||
|
||||
@ -27,6 +27,8 @@ https://www.mouser.com/ProjectManager/ProjectDetail.aspx?AccessID=c5cc83feff
|
||||
|
||||
Buy from a place where returns are easy. Ensure the photo in the listing shows a chip that says STM32 on it.
|
||||
|
||||
The BluePill must be a STM32F103**C8T6** - a C6T6 will not work. Check the listings closely.
|
||||
|
||||
https://www.amazon.com/s?k=stm32f103c8t6
|
||||
|
||||
https://www.ebay.com/sch/i.html?_nkw=stm32f103c8t6
|
||||
@ -55,6 +57,6 @@ https://github.com/erichelgeson/BlueSCSI#flashing
|
||||
|
||||
## Usage
|
||||
|
||||
Be sure to follow the usage instructions for getting your drive images setup:
|
||||
Be sure to follow the usage instructions for getting your drive images set up:
|
||||
|
||||
https://github.com/erichelgeson/BlueSCSI#usage
|
||||
|
15
lib/minIni/minGlue.h
Normal file
15
lib/minIni/minGlue.h
Normal file
@ -0,0 +1,15 @@
|
||||
/* Glue functions for the minIni library to SdFat library */
|
||||
|
||||
#include <SdFat.h>
|
||||
|
||||
extern SdFs SD;
|
||||
|
||||
#define INI_READONLY 1
|
||||
#define INI_FILETYPE FsFile
|
||||
#define ini_openread(filename,file) ((file)->open(SD.vol(), filename, O_RDONLY))
|
||||
#define ini_close(file) ((file)->close())
|
||||
#define ini_read(buffer,size,file) ((file)->fgets((buffer),(size)) > 0)
|
||||
|
||||
#define INI_FILEPOS fspos_t
|
||||
#define ini_tell(file,pos) ((file)->fgetpos(pos))
|
||||
#define ini_seek(file,pos) ((file)->fsetpos(pos))
|
952
lib/minIni/minIni.cpp
Normal file
952
lib/minIni/minIni.cpp
Normal file
@ -0,0 +1,952 @@
|
||||
/* minIni - Multi-Platform INI file parser, suitable for embedded systems
|
||||
*
|
||||
* These routines are in part based on the article "Multiplatform .INI Files"
|
||||
* by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal.
|
||||
*
|
||||
* Copyright (c) CompuPhase, 2008-2021
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Version: $Id: minIni.c 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $
|
||||
*/
|
||||
|
||||
#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY
|
||||
# if !defined UNICODE /* for Windows */
|
||||
# define UNICODE
|
||||
# endif
|
||||
# if !defined _UNICODE /* for C library */
|
||||
# define _UNICODE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define MININI_IMPLEMENTATION
|
||||
#include "minIni.h"
|
||||
#if defined NDEBUG
|
||||
#define assert(e)
|
||||
#else
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#if !defined __T || defined INI_ANSIONLY
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#define TCHAR char
|
||||
#define __T(s) s
|
||||
#define _tcscat strcat
|
||||
#define _tcschr strchr
|
||||
#define _tcscmp strcmp
|
||||
#define _tcscpy strcpy
|
||||
#define _tcsicmp stricmp
|
||||
#define _tcslen strlen
|
||||
#define _tcsncmp strncmp
|
||||
#define _tcsnicmp strnicmp
|
||||
#define _tcsrchr strrchr
|
||||
#define _tcstol strtol
|
||||
#define _tcstod strtod
|
||||
#define _totupper toupper
|
||||
#define _stprintf sprintf
|
||||
#define _tfgets fgets
|
||||
#define _tfputs fputs
|
||||
#define _tfopen fopen
|
||||
#define _tremove remove
|
||||
#define _trename rename
|
||||
#endif
|
||||
|
||||
#if defined __linux || defined __linux__
|
||||
#define __LINUX__
|
||||
#elif defined FREEBSD && !defined __FreeBSD__
|
||||
#define __FreeBSD__
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(disable: 4996) /* for Microsoft Visual C/C++ */
|
||||
#endif
|
||||
#if !defined strnicmp && !defined PORTABLE_STRNICMP
|
||||
#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ || defined __NetBSD__ || defined __DragonFly__ || defined __GNUC__
|
||||
#define strnicmp strncasecmp
|
||||
#endif
|
||||
#endif
|
||||
#if !defined _totupper
|
||||
#define _totupper toupper
|
||||
#endif
|
||||
|
||||
#if !defined INI_LINETERM
|
||||
#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ || defined __NetBSD__ || defined __DragonFly__
|
||||
#define INI_LINETERM __T("\n")
|
||||
#else
|
||||
#define INI_LINETERM __T("\r\n")
|
||||
#endif
|
||||
#endif
|
||||
#if !defined INI_FILETYPE
|
||||
#error Missing definition for INI_FILETYPE.
|
||||
#endif
|
||||
|
||||
#if !defined sizearray
|
||||
#define sizearray(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#endif
|
||||
|
||||
enum quote_option {
|
||||
QUOTE_NONE,
|
||||
QUOTE_ENQUOTE,
|
||||
QUOTE_DEQUOTE,
|
||||
};
|
||||
|
||||
#if defined PORTABLE_STRNICMP
|
||||
int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n)
|
||||
{
|
||||
while (n-- != 0 && (*s1 || *s2)) {
|
||||
register int c1, c2;
|
||||
c1 = *s1++;
|
||||
if ('a' <= c1 && c1 <= 'z')
|
||||
c1 += ('A' - 'a');
|
||||
c2 = *s2++;
|
||||
if ('a' <= c2 && c2 <= 'z')
|
||||
c2 += ('A' - 'a');
|
||||
if (c1 != c2)
|
||||
return c1 - c2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* PORTABLE_STRNICMP */
|
||||
|
||||
static TCHAR *skipleading(const TCHAR *str)
|
||||
{
|
||||
assert(str != NULL);
|
||||
while ('\0' < *str && *str <= ' ')
|
||||
str++;
|
||||
return (TCHAR *)str;
|
||||
}
|
||||
|
||||
static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base)
|
||||
{
|
||||
assert(str != NULL);
|
||||
assert(base != NULL);
|
||||
while (str > base && '\0' < *(str-1) && *(str-1) <= ' ')
|
||||
str--;
|
||||
return (TCHAR *)str;
|
||||
}
|
||||
|
||||
static TCHAR *striptrailing(TCHAR *str)
|
||||
{
|
||||
TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str);
|
||||
assert(ptr != NULL);
|
||||
*ptr = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static TCHAR *ini_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option)
|
||||
{
|
||||
size_t d, s;
|
||||
|
||||
assert(maxlen>0);
|
||||
assert(source != NULL && dest != NULL);
|
||||
assert((dest < source || (dest == source && option != QUOTE_ENQUOTE)) || dest > source + strlen(source));
|
||||
if (option == QUOTE_ENQUOTE && maxlen < 3)
|
||||
option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */
|
||||
|
||||
switch (option) {
|
||||
case QUOTE_NONE:
|
||||
for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++)
|
||||
dest[d] = source[d];
|
||||
assert(d < maxlen);
|
||||
dest[d] = '\0';
|
||||
break;
|
||||
case QUOTE_ENQUOTE:
|
||||
d = 0;
|
||||
dest[d++] = '"';
|
||||
for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) {
|
||||
if (source[s] == '"') {
|
||||
if (d >= maxlen - 3)
|
||||
break; /* no space to store the escape character plus the one that follows it */
|
||||
dest[d++] = '\\';
|
||||
}
|
||||
dest[d] = source[s];
|
||||
}
|
||||
dest[d++] = '"';
|
||||
dest[d] = '\0';
|
||||
break;
|
||||
case QUOTE_DEQUOTE:
|
||||
for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) {
|
||||
if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"')
|
||||
s++;
|
||||
dest[d] = source[s];
|
||||
}
|
||||
dest[d] = '\0';
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
static TCHAR *cleanstring(TCHAR *string, enum quote_option *quotes)
|
||||
{
|
||||
int isstring;
|
||||
TCHAR *ep;
|
||||
|
||||
assert(string != NULL);
|
||||
assert(quotes != NULL);
|
||||
|
||||
/* Remove a trailing comment */
|
||||
isstring = 0;
|
||||
for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) {
|
||||
if (*ep == '"') {
|
||||
if (*(ep + 1) == '"')
|
||||
ep++; /* skip "" (both quotes) */
|
||||
else
|
||||
isstring = !isstring; /* single quote, toggle isstring */
|
||||
} else if (*ep == '\\' && *(ep + 1) == '"') {
|
||||
ep++; /* skip \" (both quotes */
|
||||
}
|
||||
}
|
||||
assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#'));
|
||||
*ep = '\0'; /* terminate at a comment */
|
||||
striptrailing(string);
|
||||
/* Remove double quotes surrounding a value */
|
||||
*quotes = QUOTE_NONE;
|
||||
if (*string == '"' && (ep = _tcschr(string, '\0')) != NULL && *(ep - 1) == '"') {
|
||||
string++;
|
||||
*--ep = '\0';
|
||||
*quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key,
|
||||
int idxSection, int idxKey, TCHAR *Buffer, int BufferSize,
|
||||
INI_FILEPOS *mark)
|
||||
{
|
||||
TCHAR *sp, *ep;
|
||||
int len, idx;
|
||||
enum quote_option quotes;
|
||||
TCHAR LocalBuffer[INI_BUFFERSIZE];
|
||||
|
||||
assert(fp != NULL);
|
||||
/* Move through file 1 line at a time until a section is matched or EOF. If
|
||||
* parameter Section is NULL, only look at keys above the first section. If
|
||||
* idxSection is positive, copy the relevant section name.
|
||||
*/
|
||||
len = (Section != NULL) ? (int)_tcslen(Section) : 0;
|
||||
if (len > 0 || idxSection >= 0) {
|
||||
assert(idxSection >= 0 || Section != NULL);
|
||||
idx = -1;
|
||||
do {
|
||||
do {
|
||||
if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp))
|
||||
return 0;
|
||||
sp = skipleading(LocalBuffer);
|
||||
ep = _tcsrchr(sp, ']');
|
||||
} while (*sp != '[' || ep == NULL);
|
||||
/* When arrived here, a section was found; now optionally skip leading and
|
||||
* trailing whitespace.
|
||||
*/
|
||||
assert(sp != NULL && *sp == '[');
|
||||
sp = skipleading(sp + 1);
|
||||
assert(ep != NULL && *ep == ']');
|
||||
ep = skiptrailing(ep, sp);
|
||||
} while ((((int)(ep-sp) != len || Section == NULL || _tcsnicmp(sp, Section, len) != 0) && ++idx != idxSection));
|
||||
if (idxSection >= 0) {
|
||||
if (idx == idxSection) {
|
||||
assert(ep != NULL);
|
||||
*ep = '\0'; /* the end of the section name was found earlier */
|
||||
ini_strncpy(Buffer, sp, BufferSize, QUOTE_NONE);
|
||||
return 1;
|
||||
}
|
||||
return 0; /* no more section found */
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the section has been found, find the entry.
|
||||
* Stop searching upon leaving the section's area.
|
||||
*/
|
||||
assert(Key != NULL || idxKey >= 0);
|
||||
len = (Key != NULL) ? (int)_tcslen(Key) : 0;
|
||||
idx = -1;
|
||||
do {
|
||||
if (mark != NULL)
|
||||
ini_tell(fp, mark); /* optionally keep the mark to the start of the line */
|
||||
if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[')
|
||||
return 0;
|
||||
sp = skipleading(LocalBuffer);
|
||||
ep = _tcschr(sp, '='); /* Parse out the equal sign */
|
||||
if (ep == NULL)
|
||||
ep = _tcschr(sp, ':');
|
||||
} while (*sp == ';' || *sp == '#' || ep == NULL
|
||||
|| ((len == 0 || (int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey));
|
||||
if (idxKey >= 0) {
|
||||
if (idx == idxKey) {
|
||||
assert(ep != NULL);
|
||||
assert(*ep == '=' || *ep == ':');
|
||||
*ep = '\0';
|
||||
striptrailing(sp);
|
||||
ini_strncpy(Buffer, sp, BufferSize, QUOTE_NONE);
|
||||
return 1;
|
||||
}
|
||||
return 0; /* no more key found (in this section) */
|
||||
}
|
||||
|
||||
/* Copy up to BufferSize chars to buffer */
|
||||
assert(ep != NULL);
|
||||
assert(*ep == '=' || *ep == ':');
|
||||
sp = skipleading(ep + 1);
|
||||
sp = cleanstring(sp, "es); /* Remove a trailing comment */
|
||||
ini_strncpy(Buffer, sp, BufferSize, quotes);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** ini_gets()
|
||||
* \param Section the name of the section to search for
|
||||
* \param Key the name of the entry to find the value of
|
||||
* \param DefValue default string in the event of a failed read
|
||||
* \param Buffer a pointer to the buffer to copy into
|
||||
* \param BufferSize the maximum number of characters to copy
|
||||
* \param Filename the name and full path of the .ini file to read from
|
||||
*
|
||||
* \return the number of characters copied into the supplied buffer
|
||||
*/
|
||||
int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue,
|
||||
TCHAR *Buffer, int BufferSize, const TCHAR *Filename)
|
||||
{
|
||||
INI_FILETYPE fp;
|
||||
int ok = 0;
|
||||
|
||||
if (Buffer == NULL || BufferSize <= 0 || Key == NULL)
|
||||
return 0;
|
||||
if (ini_openread(Filename, &fp)) {
|
||||
ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize, NULL);
|
||||
(void)ini_close(&fp);
|
||||
}
|
||||
if (!ok)
|
||||
ini_strncpy(Buffer, (DefValue != NULL) ? DefValue : __T(""), BufferSize, QUOTE_NONE);
|
||||
return (int)_tcslen(Buffer);
|
||||
}
|
||||
|
||||
/** ini_getl()
|
||||
* \param Section the name of the section to search for
|
||||
* \param Key the name of the entry to find the value of
|
||||
* \param DefValue the default value in the event of a failed read
|
||||
* \param Filename the name of the .ini file to read from
|
||||
*
|
||||
* \return the value located at Key
|
||||
*/
|
||||
long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[64];
|
||||
int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename);
|
||||
return (len == 0) ? DefValue
|
||||
: ((len >= 2 && _totupper((int)LocalBuffer[1]) == 'X') ? _tcstol(LocalBuffer, NULL, 16)
|
||||
: _tcstol(LocalBuffer, NULL, 10));
|
||||
}
|
||||
|
||||
#if defined INI_REAL
|
||||
/** ini_getf()
|
||||
* \param Section the name of the section to search for
|
||||
* \param Key the name of the entry to find the value of
|
||||
* \param DefValue the default value in the event of a failed read
|
||||
* \param Filename the name of the .ini file to read from
|
||||
*
|
||||
* \return the value located at Key
|
||||
*/
|
||||
INI_REAL ini_getf(const TCHAR *Section, const TCHAR *Key, INI_REAL DefValue, const TCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[64];
|
||||
int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename);
|
||||
return (len == 0) ? DefValue : ini_atof(LocalBuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** ini_getbool()
|
||||
* \param Section the name of the section to search for
|
||||
* \param Key the name of the entry to find the value of
|
||||
* \param DefValue default value in the event of a failed read; it should
|
||||
* zero (0) or one (1).
|
||||
* \param Filename the name and full path of the .ini file to read from
|
||||
*
|
||||
* A true boolean is found if one of the following is matched:
|
||||
* - A string starting with 'y' or 'Y'
|
||||
* - A string starting with 't' or 'T'
|
||||
* - A string starting with '1'
|
||||
*
|
||||
* A false boolean is found if one of the following is matched:
|
||||
* - A string starting with 'n' or 'N'
|
||||
* - A string starting with 'f' or 'F'
|
||||
* - A string starting with '0'
|
||||
*
|
||||
* \return the true/false flag as interpreted at Key
|
||||
*/
|
||||
int ini_getbool(const TCHAR *Section, const TCHAR *Key, int DefValue, const TCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[2] = __T("");
|
||||
int ret;
|
||||
|
||||
ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename);
|
||||
LocalBuffer[0] = (TCHAR)_totupper((int)LocalBuffer[0]);
|
||||
if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T')
|
||||
ret = 1;
|
||||
else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F')
|
||||
ret = 0;
|
||||
else
|
||||
ret = DefValue;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/** ini_getsection()
|
||||
* \param idx the zero-based sequence number of the section to return
|
||||
* \param Buffer a pointer to the buffer to copy into
|
||||
* \param BufferSize the maximum number of characters to copy
|
||||
* \param Filename the name and full path of the .ini file to read from
|
||||
*
|
||||
* \return the number of characters copied into the supplied buffer
|
||||
*/
|
||||
int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename)
|
||||
{
|
||||
INI_FILETYPE fp;
|
||||
int ok = 0;
|
||||
|
||||
if (Buffer == NULL || BufferSize <= 0 || idx < 0)
|
||||
return 0;
|
||||
if (ini_openread(Filename, &fp)) {
|
||||
ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize, NULL);
|
||||
(void)ini_close(&fp);
|
||||
}
|
||||
if (!ok)
|
||||
*Buffer = '\0';
|
||||
return (int)_tcslen(Buffer);
|
||||
}
|
||||
|
||||
/** ini_getkey()
|
||||
* \param Section the name of the section to browse through, or NULL to
|
||||
* browse through the keys outside any section
|
||||
* \param idx the zero-based sequence number of the key to return
|
||||
* \param Buffer a pointer to the buffer to copy into
|
||||
* \param BufferSize the maximum number of characters to copy
|
||||
* \param Filename the name and full path of the .ini file to read from
|
||||
*
|
||||
* \return the number of characters copied into the supplied buffer
|
||||
*/
|
||||
int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename)
|
||||
{
|
||||
INI_FILETYPE fp;
|
||||
int ok = 0;
|
||||
|
||||
if (Buffer == NULL || BufferSize <= 0 || idx < 0)
|
||||
return 0;
|
||||
if (ini_openread(Filename, &fp)) {
|
||||
ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize, NULL);
|
||||
(void)ini_close(&fp);
|
||||
}
|
||||
if (!ok)
|
||||
*Buffer = '\0';
|
||||
return (int)_tcslen(Buffer);
|
||||
}
|
||||
|
||||
/** ini_hassection()
|
||||
* \param Section the name of the section to search for
|
||||
* \param Filename the name of the .ini file to read from
|
||||
*
|
||||
* \return 1 if the section is found, 0 if not found
|
||||
*/
|
||||
int ini_hassection(const mTCHAR *Section, const mTCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[32]; /* dummy buffer */
|
||||
INI_FILETYPE fp;
|
||||
int ok = 0;
|
||||
|
||||
if (ini_openread(Filename, &fp)) {
|
||||
ok = getkeystring(&fp, Section, NULL, -1, 0, LocalBuffer, sizearray(LocalBuffer), NULL);
|
||||
(void)ini_close(&fp);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/** ini_haskey()
|
||||
* \param Section the name of the section to search for
|
||||
* \param Key the name of the entry to find the value of
|
||||
* \param Filename the name of the .ini file to read from
|
||||
*
|
||||
* \return 1 if the key is found, 0 if not found
|
||||
*/
|
||||
int ini_haskey(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[32]; /* dummy buffer */
|
||||
INI_FILETYPE fp;
|
||||
int ok = 0;
|
||||
|
||||
if (ini_openread(Filename, &fp)) {
|
||||
ok = getkeystring(&fp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), NULL);
|
||||
(void)ini_close(&fp);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
#if !defined INI_NOBROWSE
|
||||
/** ini_browse()
|
||||
* \param Callback a pointer to a function that will be called for every
|
||||
* setting in the INI file.
|
||||
* \param UserData arbitrary data, which the function passes on the
|
||||
* \c Callback function
|
||||
* \param Filename the name and full path of the .ini file to read from
|
||||
*
|
||||
* \return 1 on success, 0 on failure (INI file not found)
|
||||
*
|
||||
* \note The \c Callback function must return 1 to continue
|
||||
* browsing through the INI file, or 0 to stop. Even when the
|
||||
* callback stops the browsing, this function will return 1
|
||||
* (for success).
|
||||
*/
|
||||
int ini_browse(INI_CALLBACK Callback, void *UserData, const TCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[INI_BUFFERSIZE];
|
||||
int lenSec, lenKey;
|
||||
enum quote_option quotes;
|
||||
INI_FILETYPE fp;
|
||||
|
||||
if (Callback == NULL)
|
||||
return 0;
|
||||
if (!ini_openread(Filename, &fp))
|
||||
return 0;
|
||||
|
||||
LocalBuffer[0] = '\0'; /* copy an empty section in the buffer */
|
||||
lenSec = (int)_tcslen(LocalBuffer) + 1;
|
||||
for ( ;; ) {
|
||||
TCHAR *sp, *ep;
|
||||
if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp))
|
||||
break;
|
||||
sp = skipleading(LocalBuffer + lenSec);
|
||||
/* ignore empty strings and comments */
|
||||
if (*sp == '\0' || *sp == ';' || *sp == '#')
|
||||
continue;
|
||||
/* see whether we reached a new section */
|
||||
ep = _tcsrchr(sp, ']');
|
||||
if (*sp == '[' && ep != NULL) {
|
||||
sp = skipleading(sp + 1);
|
||||
ep = skiptrailing(ep, sp);
|
||||
*ep = '\0';
|
||||
ini_strncpy(LocalBuffer, sp, INI_BUFFERSIZE, QUOTE_NONE);
|
||||
lenSec = (int)_tcslen(LocalBuffer) + 1;
|
||||
continue;
|
||||
}
|
||||
/* not a new section, test for a key/value pair */
|
||||
ep = _tcschr(sp, '='); /* test for the equal sign or colon */
|
||||
if (ep == NULL)
|
||||
ep = _tcschr(sp, ':');
|
||||
if (ep == NULL)
|
||||
continue; /* invalid line, ignore */
|
||||
*ep++ = '\0'; /* split the key from the value */
|
||||
striptrailing(sp);
|
||||
ini_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE);
|
||||
lenKey = (int)_tcslen(LocalBuffer + lenSec) + 1;
|
||||
/* clean up the value */
|
||||
sp = skipleading(ep);
|
||||
sp = cleanstring(sp, "es); /* Remove a trailing comment */
|
||||
ini_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes);
|
||||
/* call the callback */
|
||||
if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData))
|
||||
break;
|
||||
}
|
||||
|
||||
(void)ini_close(&fp);
|
||||
return 1;
|
||||
}
|
||||
#endif /* INI_NOBROWSE */
|
||||
|
||||
#if ! defined INI_READONLY
|
||||
static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength)
|
||||
{
|
||||
TCHAR *p;
|
||||
|
||||
ini_strncpy(dest, source, maxlength, QUOTE_NONE);
|
||||
p = _tcschr(dest, '\0');
|
||||
assert(p != NULL);
|
||||
*(p - 1) = '~';
|
||||
}
|
||||
|
||||
static enum quote_option check_enquote(const TCHAR *Value)
|
||||
{
|
||||
const TCHAR *p;
|
||||
|
||||
/* run through the value, if it has trailing spaces, or '"', ';' or '#'
|
||||
* characters, enquote it
|
||||
*/
|
||||
assert(Value != NULL);
|
||||
for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++)
|
||||
/* nothing */;
|
||||
return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE;
|
||||
}
|
||||
|
||||
static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp)
|
||||
{
|
||||
if (Section != NULL && _tcslen(Section) > 0) {
|
||||
TCHAR *p;
|
||||
LocalBuffer[0] = '[';
|
||||
ini_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */
|
||||
p = _tcschr(LocalBuffer, '\0');
|
||||
assert(p != NULL);
|
||||
*p++ = ']';
|
||||
_tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */
|
||||
if (fp != NULL)
|
||||
(void)ini_write(LocalBuffer, fp);
|
||||
}
|
||||
}
|
||||
|
||||
static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp)
|
||||
{
|
||||
TCHAR *p;
|
||||
enum quote_option option = check_enquote(Value);
|
||||
ini_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */
|
||||
p = _tcschr(LocalBuffer, '\0');
|
||||
assert(p != NULL);
|
||||
*p++ = '=';
|
||||
ini_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */
|
||||
p = _tcschr(LocalBuffer, '\0');
|
||||
assert(p != NULL);
|
||||
_tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */
|
||||
if (fp != NULL)
|
||||
(void)ini_write(LocalBuffer, fp);
|
||||
}
|
||||
|
||||
static int cache_accum(const TCHAR *string, int *size, int max)
|
||||
{
|
||||
int len = (int)_tcslen(string);
|
||||
if (*size + len >= max)
|
||||
return 0;
|
||||
*size += len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cache_flush(TCHAR *buffer, int *size,
|
||||
INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark)
|
||||
{
|
||||
int terminator_len = (int)_tcslen(INI_LINETERM);
|
||||
int pos = 0, pos_prev = -1;
|
||||
|
||||
(void)ini_seek(rfp, mark);
|
||||
assert(buffer != NULL);
|
||||
buffer[0] = '\0';
|
||||
assert(size != NULL);
|
||||
assert(*size <= INI_BUFFERSIZE);
|
||||
while (pos < *size && pos != pos_prev) {
|
||||
pos_prev = pos; /* to guard against zero bytes in the INI file */
|
||||
(void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp);
|
||||
while (pos < *size && buffer[pos] != '\0')
|
||||
pos++; /* cannot use _tcslen() because buffer may not be zero-terminated */
|
||||
}
|
||||
if (buffer[0] != '\0') {
|
||||
assert(pos > 0 && pos <= INI_BUFFERSIZE);
|
||||
if (pos == INI_BUFFERSIZE)
|
||||
pos--;
|
||||
buffer[pos] = '\0'; /* force zero-termination (may be left unterminated in the above while loop) */
|
||||
(void)ini_write(buffer, wfp);
|
||||
}
|
||||
ini_tell(rfp, mark); /* update mark */
|
||||
*size = 0;
|
||||
/* return whether the buffer ended with a line termination */
|
||||
return (pos > terminator_len) && (_tcscmp(buffer + pos - terminator_len, INI_LINETERM) == 0);
|
||||
}
|
||||
|
||||
static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, TCHAR *buffer)
|
||||
{
|
||||
(void)ini_close(rfp);
|
||||
(void)ini_close(wfp);
|
||||
(void)ini_tempname(buffer, filename, INI_BUFFERSIZE);
|
||||
#if defined ini_remove || defined INI_REMOVE
|
||||
(void)ini_remove(filename);
|
||||
#endif
|
||||
(void)ini_rename(buffer, filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** ini_puts()
|
||||
* \param Section the name of the section to write the string in
|
||||
* \param Key the name of the entry to write, or NULL to erase all keys in the section
|
||||
* \param Value a pointer to the buffer the string, or NULL to erase the key
|
||||
* \param Filename the name and full path of the .ini file to write to
|
||||
*
|
||||
* \return 1 if successful, otherwise 0
|
||||
*/
|
||||
int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename)
|
||||
{
|
||||
INI_FILETYPE rfp;
|
||||
INI_FILETYPE wfp;
|
||||
INI_FILEPOS mark;
|
||||
INI_FILEPOS head, tail;
|
||||
TCHAR *sp, *ep;
|
||||
TCHAR LocalBuffer[INI_BUFFERSIZE];
|
||||
int len, match, flag, cachelen;
|
||||
|
||||
assert(Filename != NULL);
|
||||
if (!ini_openread(Filename, &rfp)) {
|
||||
/* If the .ini file doesn't exist, make a new file */
|
||||
if (Key != NULL && Value != NULL) {
|
||||
if (!ini_openwrite(Filename, &wfp))
|
||||
return 0;
|
||||
writesection(LocalBuffer, Section, &wfp);
|
||||
writekey(LocalBuffer, Key, Value, &wfp);
|
||||
(void)ini_close(&wfp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If parameters Key and Value are valid (so this is not an "erase" request)
|
||||
* and the setting already exists, there are two short-cuts to avoid rewriting
|
||||
* the INI file.
|
||||
*/
|
||||
if (Key != NULL && Value != NULL) {
|
||||
match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head);
|
||||
if (match) {
|
||||
/* if the current setting is identical to the one to write, there is
|
||||
* nothing to do.
|
||||
*/
|
||||
if (_tcscmp(LocalBuffer,Value) == 0) {
|
||||
(void)ini_close(&rfp);
|
||||
return 1;
|
||||
}
|
||||
/* if the new setting has the same length as the current setting, and the
|
||||
* glue file permits file read/write access, we can modify in place.
|
||||
*/
|
||||
#if defined ini_openrewrite || defined INI_OPENREWRITE
|
||||
/* we already have the start of the (raw) line, get the end too */
|
||||
ini_tell(&rfp, &tail);
|
||||
/* create new buffer (without writing it to file) */
|
||||
writekey(LocalBuffer, Key, Value, NULL);
|
||||
if (_tcslen(LocalBuffer) == (size_t)(tail - head)) {
|
||||
/* length matches, close the file & re-open for read/write, then
|
||||
* write at the correct position
|
||||
*/
|
||||
(void)ini_close(&rfp);
|
||||
if (!ini_openrewrite(Filename, &wfp))
|
||||
return 0;
|
||||
(void)ini_seek(&wfp, &head);
|
||||
(void)ini_write(LocalBuffer, &wfp);
|
||||
(void)ini_close(&wfp);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* key not found, or different value & length -> proceed */
|
||||
} else if (Key != NULL && Value == NULL) {
|
||||
/* Conversely, for a request to delete a setting; if that setting isn't
|
||||
present, just return */
|
||||
match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), NULL);
|
||||
if (!match) {
|
||||
(void)ini_close(&rfp);
|
||||
return 1;
|
||||
}
|
||||
/* key found -> proceed to delete it */
|
||||
}
|
||||
|
||||
/* Get a temporary file name to copy to. Use the existing name, but with
|
||||
* the last character set to a '~'.
|
||||
*/
|
||||
(void)ini_close(&rfp);
|
||||
ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE);
|
||||
if (!ini_openwrite(LocalBuffer, &wfp))
|
||||
return 0;
|
||||
/* In the case of (advisory) file locks, ini_openwrite() may have been blocked
|
||||
* on the open, and after the block is lifted, the original file may have been
|
||||
* renamed, which is why the original file was closed and is now reopened */
|
||||
if (!ini_openread(Filename, &rfp)) {
|
||||
/* If the .ini file doesn't exist any more, make a new file */
|
||||
assert(Key != NULL && Value != NULL);
|
||||
writesection(LocalBuffer, Section, &wfp);
|
||||
writekey(LocalBuffer, Key, Value, &wfp);
|
||||
(void)ini_close(&wfp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
(void)ini_tell(&rfp, &mark);
|
||||
cachelen = 0;
|
||||
|
||||
/* Move through the file one line at a time until a section is
|
||||
* matched or until EOF. Copy to temp file as it is read.
|
||||
*/
|
||||
len = (Section != NULL) ? (int)_tcslen(Section) : 0;
|
||||
if (len > 0) {
|
||||
do {
|
||||
if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {
|
||||
/* Failed to find section, so add one to the end */
|
||||
flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
if (Key!=NULL && Value!=NULL) {
|
||||
if (!flag)
|
||||
(void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */
|
||||
writesection(LocalBuffer, Section, &wfp);
|
||||
writekey(LocalBuffer, Key, Value, &wfp);
|
||||
}
|
||||
return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */
|
||||
}
|
||||
/* Check whether this line is a section */
|
||||
sp = skipleading(LocalBuffer);
|
||||
ep = _tcsrchr(sp, ']');
|
||||
match = (*sp == '[' && ep != NULL);
|
||||
if (match) {
|
||||
/* A section was found, skip leading and trailing whitespace */
|
||||
assert(sp != NULL && *sp == '[');
|
||||
sp = skipleading(sp + 1);
|
||||
assert(ep != NULL && *ep == ']');
|
||||
ep = skiptrailing(ep, sp);
|
||||
match = ((int)(ep-sp) == len && _tcsnicmp(sp, Section, len) == 0);
|
||||
}
|
||||
/* Copy the line from source to dest, but not if this is the section that
|
||||
* we are looking for and this section must be removed
|
||||
*/
|
||||
if (!match || Key != NULL) {
|
||||
if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) {
|
||||
cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
(void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);
|
||||
cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);
|
||||
}
|
||||
}
|
||||
} while (!match);
|
||||
}
|
||||
cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
/* when deleting a section, the section head that was just found has not been
|
||||
* copied to the output file, but because this line was not "accumulated" in
|
||||
* the cache, the position in the input file was reset to the point just
|
||||
* before the section; this must now be skipped (again)
|
||||
*/
|
||||
if (Key == NULL) {
|
||||
(void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);
|
||||
(void)ini_tell(&rfp, &mark);
|
||||
}
|
||||
|
||||
/* Now that the section has been found, find the entry. Stop searching
|
||||
* upon leaving the section's area. Copy the file as it is read
|
||||
* and create an entry if one is not found.
|
||||
*/
|
||||
len = (Key != NULL) ? (int)_tcslen(Key) : 0;
|
||||
for( ;; ) {
|
||||
if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {
|
||||
/* EOF without an entry so make one */
|
||||
flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
if (Key!=NULL && Value!=NULL) {
|
||||
if (!flag)
|
||||
(void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */
|
||||
writekey(LocalBuffer, Key, Value, &wfp);
|
||||
}
|
||||
return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */
|
||||
}
|
||||
sp = skipleading(LocalBuffer);
|
||||
ep = _tcschr(sp, '='); /* Parse out the equal sign */
|
||||
if (ep == NULL)
|
||||
ep = _tcschr(sp, ':');
|
||||
match = (ep != NULL && len > 0 && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0);
|
||||
if ((Key != NULL && match) || *sp == '[')
|
||||
break; /* found the key, or found a new section */
|
||||
/* copy other keys in the section */
|
||||
if (Key == NULL) {
|
||||
(void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */
|
||||
} else {
|
||||
if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) {
|
||||
cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
(void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);
|
||||
cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* the key was found, or we just dropped on the next section (meaning that it
|
||||
* wasn't found); in both cases we need to write the key, but in the latter
|
||||
* case, we also need to write the line starting the new section after writing
|
||||
* the key
|
||||
*/
|
||||
flag = (*sp == '[');
|
||||
cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
if (Key != NULL && Value != NULL)
|
||||
writekey(LocalBuffer, Key, Value, &wfp);
|
||||
/* cache_flush() reset the "read pointer" to the start of the line with the
|
||||
* previous key or the new section; read it again (because writekey() destroyed
|
||||
* the buffer)
|
||||
*/
|
||||
(void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);
|
||||
if (flag) {
|
||||
/* the new section heading needs to be copied to the output file */
|
||||
cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);
|
||||
} else {
|
||||
/* forget the old key line */
|
||||
(void)ini_tell(&rfp, &mark);
|
||||
}
|
||||
/* Copy the rest of the INI file */
|
||||
while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) {
|
||||
if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) {
|
||||
cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
(void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp);
|
||||
cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE);
|
||||
}
|
||||
}
|
||||
cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark);
|
||||
return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */
|
||||
}
|
||||
|
||||
/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */
|
||||
#define ABS(v) ((v) < 0 ? -(v) : (v))
|
||||
|
||||
static void strreverse(TCHAR *str)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0, j = (int)_tcslen(str) - 1; i < j; i++, j--) {
|
||||
TCHAR t = str[i];
|
||||
str[i] = str[j];
|
||||
str[j] = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void long2str(long value, TCHAR *str)
|
||||
{
|
||||
int i = 0;
|
||||
long sign = value;
|
||||
|
||||
/* generate digits in reverse order */
|
||||
do {
|
||||
int n = (int)(value % 10); /* get next lowest digit */
|
||||
str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */
|
||||
} while (value /= 10); /* delete the lowest digit */
|
||||
if (sign < 0)
|
||||
str[i++] = '-';
|
||||
str[i] = '\0';
|
||||
|
||||
strreverse(str);
|
||||
}
|
||||
|
||||
/** ini_putl()
|
||||
* \param Section the name of the section to write the value in
|
||||
* \param Key the name of the entry to write
|
||||
* \param Value the value to write
|
||||
* \param Filename the name and full path of the .ini file to write to
|
||||
*
|
||||
* \return 1 if successful, otherwise 0
|
||||
*/
|
||||
int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[32];
|
||||
long2str(Value, LocalBuffer);
|
||||
return ini_puts(Section, Key, LocalBuffer, Filename);
|
||||
}
|
||||
|
||||
#if defined INI_REAL
|
||||
/** ini_putf()
|
||||
* \param Section the name of the section to write the value in
|
||||
* \param Key the name of the entry to write
|
||||
* \param Value the value to write
|
||||
* \param Filename the name and full path of the .ini file to write to
|
||||
*
|
||||
* \return 1 if successful, otherwise 0
|
||||
*/
|
||||
int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR *Filename)
|
||||
{
|
||||
TCHAR LocalBuffer[64];
|
||||
ini_ftoa(LocalBuffer, Value);
|
||||
return ini_puts(Section, Key, LocalBuffer, Filename);
|
||||
}
|
||||
#endif /* INI_REAL */
|
||||
#endif /* !INI_READONLY */
|
166
lib/minIni/minIni.h
Normal file
166
lib/minIni/minIni.h
Normal file
@ -0,0 +1,166 @@
|
||||
/* minIni - Multi-Platform INI file parser, suitable for embedded systems
|
||||
*
|
||||
* Copyright (c) CompuPhase, 2008-2021
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Version: $Id: minIni.h 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $
|
||||
*/
|
||||
#ifndef MININI_H
|
||||
#define MININI_H
|
||||
|
||||
#include "minGlue.h"
|
||||
|
||||
#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY
|
||||
#include <tchar.h>
|
||||
#define mTCHAR TCHAR
|
||||
#else
|
||||
/* force TCHAR to be "char", but only for minIni */
|
||||
#define mTCHAR char
|
||||
#endif
|
||||
|
||||
#if !defined INI_BUFFERSIZE
|
||||
#define INI_BUFFERSIZE 512
|
||||
#endif
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename);
|
||||
long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename);
|
||||
int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename);
|
||||
int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename);
|
||||
int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename);
|
||||
|
||||
int ini_hassection(const mTCHAR *Section, const mTCHAR *Filename);
|
||||
int ini_haskey(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Filename);
|
||||
|
||||
#if defined INI_REAL
|
||||
INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename);
|
||||
#endif
|
||||
|
||||
#if !defined INI_READONLY
|
||||
int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename);
|
||||
int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename);
|
||||
#if defined INI_REAL
|
||||
int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename);
|
||||
#endif
|
||||
#endif /* INI_READONLY */
|
||||
|
||||
#if !defined INI_NOBROWSE
|
||||
typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData);
|
||||
int ini_browse(INI_CALLBACK Callback, void *UserData, const mTCHAR *Filename);
|
||||
#endif /* INI_NOBROWSE */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined __cplusplus
|
||||
|
||||
#if defined __WXWINDOWS__
|
||||
#include "wxMinIni.h"
|
||||
#else
|
||||
#include <string>
|
||||
|
||||
/* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */
|
||||
class minIni
|
||||
{
|
||||
public:
|
||||
minIni(const std::string& filename) : iniFilename(filename)
|
||||
{ }
|
||||
|
||||
bool getbool(const std::string& Section, const std::string& Key, bool DefValue=false) const
|
||||
{ return ini_getbool(Section.c_str(), Key.c_str(), int(DefValue), iniFilename.c_str()) != 0; }
|
||||
|
||||
long getl(const std::string& Section, const std::string& Key, long DefValue=0) const
|
||||
{ return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); }
|
||||
|
||||
int geti(const std::string& Section, const std::string& Key, int DefValue=0) const
|
||||
{ return static_cast<int>(this->getl(Section, Key, long(DefValue))); }
|
||||
|
||||
std::string gets(const std::string& Section, const std::string& Key, const std::string& DefValue="") const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string getsection(int idx) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string getkey(const std::string& Section, int idx) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_getkey(Section.c_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool hassection(const std::string& Section) const
|
||||
{ return ini_hassection(Section.c_str(), iniFilename.c_str()) != 0; }
|
||||
|
||||
bool haskey(const std::string& Section, const std::string& Key) const
|
||||
{ return ini_haskey(Section.c_str(), Key.c_str(), iniFilename.c_str()) != 0; }
|
||||
|
||||
#if defined INI_REAL
|
||||
INI_REAL getf(const std::string& Section, const std::string& Key, INI_REAL DefValue=0) const
|
||||
{ return ini_getf(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); }
|
||||
#endif
|
||||
|
||||
#if ! defined INI_READONLY
|
||||
bool put(const std::string& Section, const std::string& Key, long Value)
|
||||
{ return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, int Value)
|
||||
{ return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, bool Value)
|
||||
{ return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, const std::string& Value)
|
||||
{ return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, const char* Value)
|
||||
{ return ini_puts(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
#if defined INI_REAL
|
||||
bool put(const std::string& Section, const std::string& Key, INI_REAL Value)
|
||||
{ return ini_putf(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; }
|
||||
#endif
|
||||
|
||||
bool del(const std::string& Section, const std::string& Key)
|
||||
{ return ini_puts(Section.c_str(), Key.c_str(), 0, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool del(const std::string& Section)
|
||||
{ return ini_puts(Section.c_str(), 0, 0, iniFilename.c_str()) != 0; }
|
||||
#endif
|
||||
|
||||
#if !defined INI_NOBROWSE
|
||||
bool browse(INI_CALLBACK Callback, void *UserData) const
|
||||
{ return ini_browse(Callback, UserData, iniFilename.c_str()) != 0; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::string iniFilename;
|
||||
};
|
||||
|
||||
#endif /* __WXWINDOWS__ */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* MININI_H */
|
@ -1,31 +1,88 @@
|
||||
; PlatformIO Project Configuration File https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:genericSTM32F103C8]
|
||||
[platformio]
|
||||
default_envs = STM32F1
|
||||
|
||||
[env]
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
SdFat=https://github.com/BlueSCSI/SdFat#2.2.0-gpt
|
||||
upload_protocol = stlink
|
||||
; Different gcc versions produce much different binaries in terms of speed.
|
||||
platform_packages = platformio/toolchain-gccarmnoneeabi@1.90301.200702
|
||||
build_flags =
|
||||
-DARDUINO_LIB_DISCOVERY_PHASE
|
||||
-DARDUINO_ARCH_STM32
|
||||
-DDEBUG_LEVEL=DEBUG_NONE
|
||||
-O2
|
||||
-D BUILD_TAGS="\"\""
|
||||
build_unflags =
|
||||
-Os
|
||||
-DARDUINO_ARCH_STM32F1
|
||||
upload_flags = -c set CPUTAPID 0
|
||||
|
||||
[env:STM32F1]
|
||||
platform = ststm32
|
||||
board = genericSTM32F103C8
|
||||
board_build.mcu = stm32f103c8t6
|
||||
board_build.core = maple
|
||||
|
||||
[env:STM32F1-XCVR]
|
||||
extends = env:STM32F1
|
||||
build_flags = ${env.build_flags}
|
||||
-DXCVR
|
||||
-D BUILD_TAGS="\"-XCVR\""
|
||||
|
||||
[env:STM32F1-USB-128MHz]
|
||||
# Max overclock for STM32
|
||||
# Can use for APM32F1 as well.
|
||||
extends = env:STM32F1-USB
|
||||
board_build.f_cpu = 128000000L
|
||||
build_flags = ${env.build_flags}
|
||||
-D BUILD_TAGS="\"-USB-128MHz\""
|
||||
|
||||
[env:STM32F1-USB-96MHz]
|
||||
# Slight overclock for STM32
|
||||
# Use for APM32F1's - it's default clock is 96MHz and runs unstable at 72MHz(STM32F1's default)
|
||||
extends = env:STM32F1-USB
|
||||
# Explicilty define the multiplier as maple only handles a few cases.
|
||||
build_flags = ${env.build_flags}
|
||||
-D BUILD_TAGS="\"-USB-96MHz\""
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
|
||||
-D USBCON
|
||||
-D USBD_VID=0x0483
|
||||
-D USB_MANUFACTURER="Unknown"
|
||||
-D USB_PRODUCT="\"BLUEPILL_F103C8\""
|
||||
-D HAL_PCD_MODULE_ENABLED
|
||||
-DBOARD_RCC_PLLMUL=RCC_PLLMUL_12 #96000000L
|
||||
|
||||
# TODO: Find out why USB build flags get trampled when extending an extended env.
|
||||
[env:STM32F1-USB]
|
||||
platform = ststm32
|
||||
board = genericSTM32F103C8
|
||||
board_build.mcu = stm32f103c8t6
|
||||
board_build.core = maple
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
greiman/SdFat @ ^2.0.6
|
||||
upload_protocol = stlink
|
||||
greiman/SdFat @ 2.2.0
|
||||
upload_protocol = dfu
|
||||
; Different gcc versions produce much different binaries in terms of speed.
|
||||
platform_packages = platformio/toolchain-gccarmnoneeabi@1.90301.200702
|
||||
|
||||
build_unflags =
|
||||
-Os
|
||||
-DARDUINO_ARCH_STM32F1
|
||||
build_flags =
|
||||
-w
|
||||
-DARDUINO_GENERIC_STM32F103C
|
||||
build_flags =
|
||||
-D BUILD_TAGS="\"-USB\""
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
|
||||
-D USBCON
|
||||
-D USBD_VID=0x0483
|
||||
-D USB_MANUFACTURER="Unknown"
|
||||
-D USB_PRODUCT="\"BLUEPILL_F103C8\""
|
||||
-D HAL_PCD_MODULE_ENABLED
|
||||
-DARDUINO_LIB_DISCOVERY_PHASE
|
||||
-DARDUINO=10813
|
||||
-DARDUINO_ARCH_STM32
|
||||
-DDEBUG_LEVEL=DEBUG_NONE
|
||||
-O2
|
||||
|
||||
; Work around for clones.
|
||||
; https://community.platformio.org/t/cannot-upload-to-stm32-bluepill-board-over-stlink-v2/3492/25
|
||||
build_unflags =
|
||||
-Os
|
||||
-DARDUINO_ARCH_STM32F1
|
||||
upload_flags = -c set CPUTAPID 0
|
||||
|
||||
; [env:debug]
|
||||
|
2888
src/BlueSCSI.cpp
2888
src/BlueSCSI.cpp
File diff suppressed because it is too large
Load Diff
371
src/BlueSCSI.h
Normal file
371
src/BlueSCSI.h
Normal file
@ -0,0 +1,371 @@
|
||||
#ifndef __BLUESCSI_H__
|
||||
#define __BLUESCSI_H__
|
||||
|
||||
#include <Arduino.h> // For Platform.IO
|
||||
#include <SdFat.h>
|
||||
|
||||
// SCSI config
|
||||
#define MAX_SCSIID 7 // Maximum number of supported SCSI-IDs (The minimum is 0)
|
||||
#define MAX_SCSILUN 8 // Maximum number of LUNs supported (The minimum is 0)
|
||||
#define NUM_SCSIID MAX_SCSIID // Number of enabled SCSI IDs
|
||||
#define NUM_SCSILUN 1 // Number of enabled LUNs
|
||||
#define READ_PARITY_CHECK 0 // Perform read parity check (unverified)
|
||||
#define DEFAULT_SCSI_ID 1
|
||||
#define DEFAULT_SCSI_LUN 0
|
||||
#define SCSI_BUF_SIZE 512 // Size of the SCSI Buffer
|
||||
#define HDD_BLOCK_SIZE 512
|
||||
#define OPTICAL_BLOCK_SIZE 2048
|
||||
#define BLUESCSI_INI "bluescsi.ini"
|
||||
|
||||
#define SCSI_VENDOR_LENGTH 8
|
||||
#define SCSI_PRODUCT_LENGTH 16
|
||||
#define SCSI_REVISION_LENGTH 4
|
||||
|
||||
// HDD format
|
||||
#define MAX_BLOCKSIZE 4096 // Maximum BLOCK size
|
||||
|
||||
// SDFAT
|
||||
#define SD1_CONFIG SdSpiConfig(PA4, DEDICATED_SPI, SD_SCK_MHZ(SPI_FULL_SPEED), &SPI)
|
||||
|
||||
// LED ERRORS
|
||||
#define ERROR_FALSE_INIT 3
|
||||
#define ERROR_NO_SDCARD 5
|
||||
|
||||
enum SCSI_DEVICE_TYPE
|
||||
{
|
||||
SCSI_DEVICE_HDD,
|
||||
SCSI_DEVICE_OPTICAL,
|
||||
};
|
||||
|
||||
#define SCSI_DEVICE_FLAG_OPTICAL_MODE2 0x1
|
||||
#define SCSI_DEVICE_FLAG_OPTICAL_RAW 0x2
|
||||
|
||||
#define SET_DEVICE_FLAG(var, flag) (var |= flag)
|
||||
#define UNSET_DEVICE_FLAG(var, flag) (var &= ~flag)
|
||||
#define IS_DEVICE_FLAG_SET(var, flag) ((var & flag) == flag)
|
||||
#define IS_RAW(var) IS_DEVICE_FLAG_SET(var, SCSI_DEVICE_FLAG_OPTICAL_RAW)
|
||||
#define IS_MODE2(var) IS_DEVICE_FLAG_SET(var, SCSI_DEVICE_FLAG_OPTICAL_MODE2)
|
||||
|
||||
#define INT_TO_CHAR(var) var+'0'
|
||||
#define CHAR_TO_INT(var) var-'0'
|
||||
|
||||
#define CDROM_RAW_SECTORSIZE 2352
|
||||
#define CDROM_COMMON_SECTORSIZE 2048
|
||||
|
||||
#define MAX_SCSI_COMMAND 0xff
|
||||
#define SCSI_COMMAND_HANDLER(x) static byte x(SCSI_DEVICE *dev, const byte *cdb)
|
||||
|
||||
#if DEBUG
|
||||
#define LOG(XX) Serial.print(XX)
|
||||
#define LOGHEX(XX) Serial.print(XX, HEX)
|
||||
#define LOGN(XX) Serial.println(XX)
|
||||
#define LOGHEXN(XX) Serial.println(XX, HEX)
|
||||
#else
|
||||
#define LOG(XX) //Serial.print(XX)
|
||||
#define LOGHEX(XX) //Serial.print(XX, HEX)
|
||||
#define LOGN(XX) //Serial.println(XX)
|
||||
#define LOGHEXN(XX) //Serial.println(XX, HEX)
|
||||
#endif
|
||||
|
||||
#define active 1
|
||||
#define inactive 0
|
||||
#define high 0
|
||||
#define low 1
|
||||
|
||||
#define isHigh(XX) ((XX) == high)
|
||||
#define isLow(XX) ((XX) != high)
|
||||
|
||||
#define gpio_mode(pin,val) gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val);
|
||||
#define gpio_write(pin,val) gpio_write_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val)
|
||||
#define gpio_read(pin) gpio_read_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit)
|
||||
|
||||
//#define DB0 PB8 // SCSI:DB0
|
||||
//#define DB1 PB9 // SCSI:DB1
|
||||
//#define DB2 PB10 // SCSI:DB2
|
||||
//#define DB3 PB11 // SCSI:DB3
|
||||
//#define DB4 PB12 // SCSI:DB4
|
||||
//#define DB5 PB13 // SCSI:DB5
|
||||
//#define DB6 PB14 // SCSI:DB6
|
||||
//#define DB7 PB15 // SCSI:DB7
|
||||
//#define DBP PB0 // SCSI:DBP
|
||||
#define ATN PA8 // SCSI:ATN
|
||||
#define BSY PA9 // SCSI:BSY
|
||||
#define ACK PA10 // SCSI:ACK
|
||||
#define RST PA15 // SCSI:RST
|
||||
#define MSG PB3 // SCSI:MSG
|
||||
#define SEL PB4 // SCSI:SEL
|
||||
#define CD PB5 // SCSI:C/D
|
||||
#define REQ PB6 // SCSI:REQ
|
||||
#define IO PB7 // SCSI:I/O
|
||||
|
||||
#define SD_CS PA4 // SDCARD:CS
|
||||
|
||||
// Image Set Selector
|
||||
#ifdef XCVR
|
||||
#define IMAGE_SELECT1 PC14
|
||||
#define IMAGE_SELECT2 PC15
|
||||
#else
|
||||
#define IMAGE_SELECT1 PA1
|
||||
#define IMAGE_SELECT2 PB1
|
||||
#endif
|
||||
|
||||
// GPIO register port
|
||||
#define PAREG GPIOA->regs
|
||||
#define PBREG GPIOB->regs
|
||||
#define PCREG GPIOC->regs
|
||||
|
||||
// Virtual pin (Arduio compatibility is slow, so make it MCU-dependent)
|
||||
#define PA(BIT) (BIT)
|
||||
#define PB(BIT) (BIT+16)
|
||||
#define PC(BIT) (BIT+32)
|
||||
|
||||
// Virtual pin decoding
|
||||
#define GPIOREG(VPIN) ((VPIN)>=16?((VPIN)>=32?PCREG:PBREG):PAREG)
|
||||
#define BITMASK(VPIN) (1<<((VPIN)&15))
|
||||
|
||||
// Built-in LED
|
||||
#define LED PC13
|
||||
#define vLED PC(13)
|
||||
#define LED_MODE GPIO_OUTPUT_OD
|
||||
|
||||
// External LED
|
||||
#define LED2 PA0
|
||||
#define vLED2 PA(0)
|
||||
#define LED2_MODE GPIO_OUTPUT_PP
|
||||
|
||||
// LED control
|
||||
#define LED_ON() GPIOREG(vLED)->BSRR = BITMASK(vLED) << (LED_MODE == GPIO_OUTPUT_PP ? 0 : 16); GPIOREG(vLED2)->BSRR = BITMASK(vLED2) << (LED2_MODE == GPIO_OUTPUT_PP ? 0 : 16);
|
||||
#define LED_OFF() GPIOREG(vLED)->BSRR = BITMASK(vLED) << (LED_MODE == GPIO_OUTPUT_PP ? 16 : 0); GPIOREG(vLED2)->BSRR = BITMASK(vLED2) << (LED2_MODE == GPIO_OUTPUT_PP ? 16 : 0);
|
||||
|
||||
#define vATN PA(8) // SCSI:ATN
|
||||
#define vBSY PA(9) // SCSI:BSY
|
||||
#define vACK PA(10) // SCSI:ACK
|
||||
#define vRST PA(15) // SCSI:RST
|
||||
#define vMSG PB(3) // SCSI:MSG
|
||||
#define vSEL PB(4) // SCSI:SEL
|
||||
#define vCD PB(5) // SCSI:C/D
|
||||
#define vREQ PB(6) // SCSI:REQ
|
||||
#define vIO PB(7) // SCSI:I/O
|
||||
#define vSD_CS PA(4) // SDCARD:CS
|
||||
|
||||
// SCSI output pin control: opendrain active LOW (direct pin drive)
|
||||
#define SCSI_OUT(VPIN,ACTIVE) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN)<<((ACTIVE)?16:0); }
|
||||
|
||||
// SCSI input pin check (inactive=0,active=1)
|
||||
#define SCSI_IN(VPIN) ((~GPIOREG(VPIN)->IDR>>(VPIN&15))&1)
|
||||
|
||||
#define NOP(x) for(unsigned _nopcount = x; _nopcount; _nopcount--) { asm("NOP"); }
|
||||
|
||||
/* SCSI Timing delays */
|
||||
// Due to limitations in timing granularity all of these are "very" rough estimates
|
||||
#define SCSI_BUS_SETTLE() NOP(30); // spec 400ns ours ~420us
|
||||
#define SCSI_DATA_RELEASE() NOP(30); // spec 400ns ours ~420us
|
||||
#define SCSI_HOLD_TIME() asm("NOP"); asm("NOP"); asm("NOP"); // spec 45ns ours ~42ns
|
||||
#define SCSI_DESKEW() // asm("NOP"); asm("NOP"); asm("NOP"); // spec 45ns ours ~42ns
|
||||
#define SCSI_CABLE_SKEW() // asm("NOP"); // spec 10ns ours ~14ns
|
||||
#define SCSI_RESET_HOLD() asm("NOP"); asm("NOP"); // spec 25ns ours ~28ns
|
||||
#define SCSI_DISCONNECTION_DELAY() NOP(15); // spec 200ns ours ~210ns
|
||||
|
||||
/* SCSI phases
|
||||
+=============-===============-==================================-============+
|
||||
| Signal | Phase name | Direction of transfer | Comment |
|
||||
|-------------| | | |
|
||||
| MSG|C/D|I/O | | | |
|
||||
|----+---+----+---------------+----------------------------------+------------|
|
||||
| 0 | 0 | 0 | DATA OUT | Initiator to target \ | Data |
|
||||
| 0 | 0 | 1 | DATA IN | Initiator from target / | phase |
|
||||
| 0 | 1 | 0 | COMMAND | Initiator to target | |
|
||||
| 0 | 1 | 1 | STATUS | Initiator from target | |
|
||||
| 1 | 0 | 0 | * | | |
|
||||
| 1 | 0 | 1 | * | | |
|
||||
| 1 | 1 | 0 | MESSAGE OUT | Initiator to target \ | Message |
|
||||
| 1 | 1 | 1 | MESSAGE IN | Initiator from target / | phase |
|
||||
|-----------------------------------------------------------------------------|
|
||||
| Key: 0 = False, 1 = True, * = Reserved for future standardization |
|
||||
+=============================================================================+
|
||||
*/
|
||||
// SCSI phase change as single write to port B
|
||||
#define SCSIPHASEMASK(MSGACTIVE, CDACTIVE, IOACTIVE) ((BITMASK(vMSG)<<((MSGACTIVE)?16:0)) | (BITMASK(vCD)<<((CDACTIVE)?16:0)) | (BITMASK(vIO)<<((IOACTIVE)?16:0)))
|
||||
|
||||
#define SCSI_PHASE_DATAOUT SCSIPHASEMASK(inactive, inactive, inactive)
|
||||
#define SCSI_PHASE_DATAIN SCSIPHASEMASK(inactive, inactive, active)
|
||||
#define SCSI_PHASE_COMMAND SCSIPHASEMASK(inactive, active, inactive)
|
||||
#define SCSI_PHASE_STATUS SCSIPHASEMASK(inactive, active, active)
|
||||
#define SCSI_PHASE_MESSAGEOUT SCSIPHASEMASK(active, active, inactive)
|
||||
#define SCSI_PHASE_MESSAGEIN SCSIPHASEMASK(active, active, active)
|
||||
|
||||
#define SCSI_PHASE_CHANGE(MASK) { PBREG->BSRR = (MASK); }
|
||||
|
||||
#ifdef XCVR
|
||||
#define TR_TARGET PA1 // Target Transceiver Control Pin
|
||||
#define TR_DBP PA2 // Data Pins Transceiver Control Pin
|
||||
#define TR_INITIATOR PA3 // Initiator Transciever Control Pin
|
||||
|
||||
#define vTR_TARGET PA(1) // Target Transceiver Control Pin
|
||||
#define vTR_DBP PA(2) // Data Pins Transceiver Control Pin
|
||||
#define vTR_INITIATOR PA(3) // Initiator Transciever Control Pin
|
||||
|
||||
#define TR_INPUT 1
|
||||
#define TR_OUTPUT 0
|
||||
|
||||
// Transceiver control definitions
|
||||
#define TRANSCEIVER_IO_SET(VPIN,TR_INPUT) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN) << ((TR_INPUT) ? 16 : 0); }
|
||||
|
||||
// Turn on the output only for BSY
|
||||
#define SCSI_BSY_ACTIVE() { gpio_mode(BSY, GPIO_OUTPUT_PP); SCSI_OUT(vBSY, active) }
|
||||
|
||||
#define SCSI_TARGET_ACTIVE() { gpio_mode(REQ, GPIO_OUTPUT_PP); gpio_mode(MSG, GPIO_OUTPUT_PP); gpio_mode(CD, GPIO_OUTPUT_PP); gpio_mode(IO, GPIO_OUTPUT_PP); gpio_mode(BSY, GPIO_OUTPUT_PP); TRANSCEIVER_IO_SET(vTR_TARGET,TR_OUTPUT);}
|
||||
// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
|
||||
#define SCSI_TARGET_INACTIVE() { gpio_mode(REQ, GPIO_INPUT_FLOATING); gpio_mode(MSG, GPIO_INPUT_FLOATING); gpio_mode(CD, GPIO_INPUT_FLOATING); gpio_mode(IO, GPIO_INPUT_FLOATING); gpio_mode(BSY, GPIO_INPUT_FLOATING); TRANSCEIVER_IO_SET(vTR_TARGET,TR_INPUT); }
|
||||
|
||||
#define DB_MODE_OUT 1 // push-pull mode
|
||||
#define DB_MODE_IN 4 // floating inputs
|
||||
|
||||
#else
|
||||
|
||||
// GPIO mode
|
||||
// IN , FLOAT : 4
|
||||
// IN , PU/PD : 8
|
||||
// OUT, PUSH/PULL : 3
|
||||
// OUT, OD : 1
|
||||
#define DB_MODE_OUT 3
|
||||
//#define DB_MODE_OUT 7
|
||||
#define DB_MODE_IN 8
|
||||
|
||||
|
||||
// Turn on the output only for BSY
|
||||
#define SCSI_BSY_ACTIVE() { gpio_mode(BSY, GPIO_OUTPUT_OD); SCSI_OUT(vBSY, active) }
|
||||
// BSY,REQ,MSG,CD,IO Turn on the output (no change required for OD)
|
||||
#define SCSI_TARGET_ACTIVE() { if (DB_MODE_OUT != 7) gpio_mode(REQ, GPIO_OUTPUT_PP); }
|
||||
// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
|
||||
#define SCSI_TARGET_INACTIVE() { if (DB_MODE_OUT == 7) SCSI_OUT(vREQ,inactive) else { if (DB_MODE_IN == 8) gpio_mode(REQ, GPIO_INPUT_PU) else gpio_mode(REQ, GPIO_INPUT_FLOATING)} PBREG->BSRR = 0b000000000000000011101000; SCSI_OUT(vBSY,inactive); gpio_mode(BSY, GPIO_INPUT_PU); }
|
||||
|
||||
#endif
|
||||
|
||||
// Put DB and DP in output mode
|
||||
#define SCSI_DB_OUTPUT() { PBREG->CRL=(PBREG->CRL &0xfffffff0)|DB_MODE_OUT; PBREG->CRH = 0x11111111*DB_MODE_OUT; }
|
||||
// Put DB and DP in input mode
|
||||
#define SCSI_DB_INPUT() { PBREG->CRL=(PBREG->CRL &0xfffffff0)|DB_MODE_IN ; PBREG->CRH = (uint32_t)0x11111111*DB_MODE_IN; }
|
||||
|
||||
// HDDiamge file
|
||||
#define HDIMG_ID_POS 2 // Position to embed ID number
|
||||
#define HDIMG_LUN_POS 3 // Position to embed LUN numbers
|
||||
#define HDIMG_BLK_POS 5 // Position to embed block size numbers
|
||||
#define MAX_FILE_PATH 64 // Maximum file name length
|
||||
#define MAX_MAC_PATH 32 // Maximum file name length on vintage macs
|
||||
#define MAC_BLK_SIZE 4096
|
||||
/*
|
||||
* Data byte to BSRR register setting value and parity table
|
||||
*/
|
||||
|
||||
// Parity bit generation
|
||||
//#define PTY(V) (1^((V)^((V)>>1)^((V)>>2)^((V)>>3)^((V)>>4)^((V)>>5)^((V)>>6)^((V)>>7))&1)
|
||||
#define PTY(n) ((1 ^ (n) ^ ((n)>>1) ^ ((n)>>2) ^ ((n)>>3) ^ ((n)>>4) ^ ((n)>>5) ^ ((n)>>6) ^ ((n)>>7)) & 1)
|
||||
|
||||
// Data byte to BSRR register setting value conversion table
|
||||
// BSRR[31:24] = DB[7:0]
|
||||
// BSRR[ 16] = PTY(DB)
|
||||
// BSRR[15: 8] = ~DB[7:0]
|
||||
// BSRR[ 0] = ~PTY(DB)
|
||||
|
||||
// Set DBP, set REQ = inactive
|
||||
#define DBP(D) ((((((uint32_t)(D)<<8)|PTY(D))*0x00010001)^0x0000ff01)|BITMASK(vREQ))
|
||||
//#define DBP(D) ((((((uint32_t)(D)<<8)|PTY(D))*0x00010001)^0x0000ff01))
|
||||
|
||||
|
||||
//#define DBP8(D) DBP(D),DBP(D+1),DBP(D+2),DBP(D+3),DBP(D+4),DBP(D+5),DBP(D+6),DBP(D+7)
|
||||
//#define DBP32(D) DBP8(D),DBP8(D+8),DBP8(D+16),DBP8(D+24)
|
||||
|
||||
// BSRR register control value that simultaneously performs DB set, DP set, and REQ = H (inactrive)
|
||||
uint32_t db_bsrr[256];
|
||||
// Parity bit acquisition
|
||||
#define PARITY(DB) (db_bsrr[DB]&1)
|
||||
|
||||
// Macro cleaning
|
||||
//#undef DBP32
|
||||
//#undef DBP8
|
||||
//#undef DBP
|
||||
//#undef PTY
|
||||
|
||||
// #define GET_CDB6_LBA(x) ((x[2] & 01f) << 16) | (x[3] << 8) | x[4]
|
||||
#define READ_DATA_BUS() (byte)((~(uint32_t)GPIOB->regs->IDR)>>8)
|
||||
|
||||
|
||||
|
||||
typedef struct _SCSI_INQUIRY_DATA
|
||||
{
|
||||
union
|
||||
{
|
||||
struct {
|
||||
// bitfields are in REVERSE order for ARM
|
||||
// byte 0
|
||||
byte peripheral_device_type:5;
|
||||
byte peripheral_qualifier:3;
|
||||
// byte 1
|
||||
byte reserved_byte2:7;
|
||||
byte rmb:1;
|
||||
// byte 2
|
||||
byte ansi_version:3;
|
||||
byte always_zero_byte3:5;
|
||||
// byte 3
|
||||
byte response_format:4;
|
||||
byte reserved_byte4:2;
|
||||
byte tiop:1;
|
||||
byte always_zero_byte4:1;
|
||||
// byte 4
|
||||
byte additional_length;
|
||||
// byte 5-6
|
||||
byte reserved_byte5;
|
||||
byte reserved_byte6;
|
||||
// byte 7
|
||||
byte sync:1;
|
||||
byte always_zero_byte7_more:4;
|
||||
byte always_zero_byte7:3;
|
||||
// byte 8-15
|
||||
char vendor[8];
|
||||
// byte 16-31
|
||||
char product[16];
|
||||
// byte 32-35
|
||||
char revision[4];
|
||||
// byte 36
|
||||
byte release;
|
||||
// 37-46
|
||||
char revision_date[10];
|
||||
};
|
||||
// raw bytes
|
||||
byte raw[64];
|
||||
};
|
||||
} SCSI_INQUIRY_DATA;
|
||||
|
||||
// HDD image
|
||||
typedef __attribute__((aligned(4))) struct _SCSI_DEVICE
|
||||
{
|
||||
FsFile m_file; // File object
|
||||
uint64_t m_fileSize; // File size
|
||||
uint16_t m_blocksize; // SCSI BLOCK size
|
||||
uint16_t m_rawblocksize; // OPTICAL raw sector size
|
||||
uint8_t m_type; // SCSI device type
|
||||
uint32_t m_blockcount; // blockcount
|
||||
SCSI_INQUIRY_DATA inquiry_block; // SCSI information
|
||||
uint8_t m_senseKey; // Sense key
|
||||
uint16_t m_additional_sense_code; // ASC/ASCQ
|
||||
uint8_t m_sector_offset; // optical sector offset for missing sync header
|
||||
uint8_t flags; // various device flags
|
||||
} SCSI_DEVICE;
|
||||
|
||||
static byte cdb_len_lookup[] = {
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,16,16,16,16,16,16,16,16,
|
||||
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
|
||||
16,16,16,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
|
||||
12,12,12,12,12,12,12,12,12,12,12,12,12,12,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
|
||||
10,10,10,10,10,10,10,10,10,10,10,10,10,10
|
||||
};
|
||||
|
||||
#endif
|
93
src/scsi_cmds.h
Normal file
93
src/scsi_cmds.h
Normal file
@ -0,0 +1,93 @@
|
||||
#ifndef __SCSI_CMDS_H__
|
||||
#define __SCSI_CMDS_H__
|
||||
|
||||
// defines for SCSI commands
|
||||
#define SCSI_TEST_UNIT_READY 0
|
||||
#define SCSI_REZERO_UNIT 0x1
|
||||
#define SCSI_REQUEST_SENSE 0x3
|
||||
#define SCSI_FORMAT_UNIT4 0x4
|
||||
#define SCSI_FORMAT_UNIT6 0x6
|
||||
#define SCSI_REASSIGN_BLOCKS 0x7
|
||||
#define SCSI_READ6 0x8
|
||||
#define SCSI_WRITE6 0xA
|
||||
#define SCSI_SEEK6 0xB
|
||||
#define SCSI_INQUIRY 0x12
|
||||
#define SCSI_MODE_SELECT6 0x15
|
||||
#define SCSI_RESERVE 0x16
|
||||
#define SCSI_RELEASE 0x17
|
||||
#define SCSI_COPY 0x18
|
||||
#define SCSI_MODE_SENSE6 0x1A
|
||||
#define SCSI_START_STOP_UNIT 0x1B
|
||||
#define SCSI_RECV_DIAG_RESULTS 0x1C
|
||||
#define SCSI_SEND_DIAG 0x1D
|
||||
#define SCSI_PREVENT_ALLOW_REMOVAL 0x1E
|
||||
#define SCSI_ICD_EXTENDED_CMD 0x1F
|
||||
#define SCSI_READ_CAPACITY 0x25
|
||||
#define SCSI_READ10 0x28
|
||||
#define SCSI_WRITE10 0x2A
|
||||
#define SCSI_SEEK10 0x2B
|
||||
#define SCSI_WRITE_AND_VERIFY 0x2E
|
||||
#define SCSI_VERIFY10 0x2F
|
||||
#define SCSI_SEARCH_DATA_HIGH 0x30
|
||||
#define SCSI_SEARCH_DATA_EQUAL 0x31
|
||||
#define SCSI_SEARCH_DATA_LOW 0x32
|
||||
#define SCSI_SET_LIMITS 0x33
|
||||
#define SCSI_PREFETCH 0x34
|
||||
#define SCSI_SYNCHRONIZE_CACHE 0x35
|
||||
#define SCSI_LOCK_UNLOCK_CACHE 0x36
|
||||
#define SCSI_READ_DEFECT_DATA 0x37
|
||||
#define SCSI_COMPARE 0x39
|
||||
#define SCSI_COPY_AND_VERIFY 0x3A
|
||||
#define SCSI_WRITE_BUFFER 0x3B
|
||||
#define SCSI_READ_BUFFER 0x3C
|
||||
#define SCSI_READ_LONG 0x3E
|
||||
#define SCSI_WRITE_LONG 0x3F
|
||||
#define SCSI_CHANGE_DEFINITION 0x40
|
||||
#define SCSI_WRITE_SAME 0x41
|
||||
#define SCSI_LOG_SELECT 0x4C
|
||||
#define SCSI_LOG_SENSE 0x4D
|
||||
#define SCSI_MODE_SELECT10 0x55
|
||||
#define SCSI_MODE_SENSE10 0x5A
|
||||
#define SCSI_READ12 0xA8
|
||||
#define SCSI_VERIFY12 0xAF
|
||||
|
||||
#define BLUESCSI_LIST_FILES 0xD0
|
||||
#define BLUESCSI_GET_FILE 0xD1
|
||||
#define BLUESCSI_COUNT_FILES 0xD2
|
||||
#define BLUESCSI_SEND_PREP 0xD3
|
||||
#define BLUESCSI_SEND 0xD4
|
||||
#define BLUESCSI_SEND_END 0xD5
|
||||
|
||||
|
||||
|
||||
#define SCSI_TOC_LENGTH 20 // length for default CDROM TOC
|
||||
|
||||
// SCSI CDROM commands
|
||||
#define SCSI_AUDIO_SCAN1 0xBA
|
||||
#define SCSI_AUDIO_SCAN2 0xCD
|
||||
#define SCSI_PAUSE_RESUME 0x4B
|
||||
#define SCSI_PLAY_AUDIO10 0x45
|
||||
#define SCSI_PLAY_AUDIO12 0xA5
|
||||
#define SCSI_PLAY_AUDIO_MSF 0x47
|
||||
#define SCSI_PLAY_AUDIO_TRACK_IDX 0x48
|
||||
#define SCSI_PLAY_TRACK_RELATIVE10 0x49
|
||||
#define SCSI_PLAY_TRACK_RELATIVE12 0xA9
|
||||
#define SCSI_READ_CD 0xBE
|
||||
#define SCSI_READ_CD_DD 0xD8
|
||||
#define SCSI_READ_CD_MSF 0xB9
|
||||
#define SCSI_READ_CDDA_MSF 0xD9
|
||||
#define SCSI_READ_CDXA 0xDB
|
||||
#define SCSI_READ_ALL_SUBCODE 0xDF
|
||||
#define SCSI_READ_HEADER 0x44
|
||||
#define SCSI_READ_SUBCHANNEL 0x42
|
||||
#define SCSI_READ_TOC 0x43
|
||||
#define SCSI_READ_DISC_INFORMATION 0x51
|
||||
#define SCSI_READ_DVD_STRUCTURE 0xAD
|
||||
#define SCSI_SET_CDROM_SPEED1 0xBB
|
||||
#define SCSI_SET_CDROM_SPEED2 0xDA
|
||||
#define SCSI_STOP_PLAY_SCAN 0x4E
|
||||
#define SCSI_READ_CDP 0xE4
|
||||
#define SCSI_READ_DRIVE_STATUS 0xE0
|
||||
#define SCSI_WRITE_CDP 0xE3
|
||||
|
||||
#endif // __SCSI_CMDS_H__
|
9
src/scsi_mode.h
Normal file
9
src/scsi_mode.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef __SCSI_MODE_H__
|
||||
#define __SCSI_MODE_H__
|
||||
|
||||
#define MODE_COMBINED_HEADER_DATA 0x00
|
||||
#define MODE_NOT_SUPPORTED 0x01
|
||||
#define MODE_DATA 0x02
|
||||
#define MODE_DESCRIPTOR 0x03
|
||||
|
||||
#endif
|
55
src/scsi_sense.h
Normal file
55
src/scsi_sense.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef __SCSI_SENSE_H__
|
||||
#define __SCSI_SENSE_H__
|
||||
|
||||
#define SCSI_SENSE_NO_SENSE 0
|
||||
#define SCSI_SENSE_RECOVERED_ERROR 0x1
|
||||
#define SCSI_SENSE_NOT_READY 0x2
|
||||
#define SCSI_SENSE_MEDUIM_ERROR 0x3
|
||||
#define SCSI_SENSE_HARDWARE_ERROR 0x4
|
||||
#define SCSI_SENSE_ILLEGAL_REQUEST 0x5
|
||||
#define SCSI_SENSE_UNIT_ATTENTION 0x6
|
||||
#define SCSI_SENSE_DATA_PROTECT 0x7
|
||||
#define SCSI_SENSE_BLANK_CHECK 0x8
|
||||
#define SCSI_SENSE_VENDOR_SPECIFIC 0x9
|
||||
#define SCSI_SENSE_COPY_ABORTED 0xa
|
||||
#define SCSI_SENSE_ABORTED_COMMAND 0xb
|
||||
#define SCSI_SENSE_EQUAL 0xc
|
||||
#define SCSI_SENSE_VOLUME_OVERFLOW 0xd
|
||||
#define SCSI_SENSE_MISCOMPARE 0xe
|
||||
#define SCSI_SENSE_RESERVED 0xf
|
||||
|
||||
|
||||
#define SCSI_ASC_INVALID_OPERATION_CODE 0x2000
|
||||
#define SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x2100
|
||||
#define SCSI_ASC_INVALID_FIELD_IN_CDB 0x2400
|
||||
#define SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED 0x2500
|
||||
#define SCSI_ASC_INVALID_FIELD_PARAMETER_LIST 0x2600
|
||||
#define SCSI_ASC_WRITE_PROTECTED 0x2700
|
||||
#define SCSI_ASC_CANNOT_READ_MEDIUM_UNKNOWN_FORMAT 0x3001
|
||||
#define SCSI_ASC_CANNOT_READ_MEDIUM_INCOMPATIBLE_FORMAT 0x3002
|
||||
#define SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x3900
|
||||
#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A00
|
||||
#define SCSI_ASC_LUN_NOT_READY_MANUAL_INTERVENTION_REQUIRED 0x0403
|
||||
|
||||
// OpenRetroSCSI Vendor Sense
|
||||
#define OPEN_RETRO_SCSI_TOO_MANY_FILES 0x0001
|
||||
|
||||
|
||||
// SCSI mode page codes
|
||||
#define SCSI_SENSE_MODE_VENDOR 0x00
|
||||
#define SCSI_SENSE_MODE_READ_WRITE_ERROR_RECOVERY 0x01
|
||||
#define SCSI_SENSE_MODE_DISCONNECT_RECONNECT 0x02
|
||||
#define SCSI_SENSE_MODE_FORMAT_DEVICE 0x03
|
||||
#define SCSI_SENSE_MODE_DISK_GEOMETRY 0x04
|
||||
#define SCSI_SENSE_MODE_FLEXABLE_GEOMETRY 0x05
|
||||
#define SCSI_SENSE_MODE_CACHING 0x08
|
||||
#define SCSI_SENSE_MODE_CDROM 0x0D
|
||||
#define SCSI_SENSE_MODE_CDROM_AUDIO_CONTROL 0x0E
|
||||
#define SCSI_SENSE_MODE_VENDOR_APPLE 0x30
|
||||
|
||||
#define SCSI_SENSE_MODE_VENDOR_BLUESCSI 0x31 // Lookup range for vndr
|
||||
|
||||
#define SCSI_SENSE_MODE_ALL 0x3F
|
||||
|
||||
|
||||
#endif
|
14
src/scsi_status.h
Normal file
14
src/scsi_status.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __SCSI_STATUS_H__
|
||||
#define __SCSI_STATUS_H__
|
||||
|
||||
#define SCSI_STATUS_GOOD 0
|
||||
#define SCSI_STATUS_CHECK_CONDITION 0x2
|
||||
#define SCSI_STATUS_CONDITION_MET 0x4
|
||||
#define SCSI_STATUS_BUSY 0x8
|
||||
#define SCSI_STATUS_INTERMEDIATE 0x16
|
||||
#define SCSI_STATUS_INTERMEDIATE_CONDITION_MET 0x20
|
||||
#define SCSI_STATUS_RESERVATION_CONFLICT 0x24
|
||||
#define SCSI_STATUS_COMMAND_TERMINATED 0x34
|
||||
#define SCSI_STATUS_QUEUE_FULL 0x40
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user