Compare commits

...

82 Commits

Author SHA1 Message Date
Eric Helgeson bf5823468a Add 50pin 1.1 case back, was accidentally removed in 57ff0cd3b4 2024-02-22 07:30:57 -06:00
Eric Helgeson 2baa0155e1
Merge pull request #262 from dotsam/better-logging
Reformatted Logging
2024-02-17 15:59:33 -06:00
Sam Edwards 3a9db99723 Changing indentation on log lines 2024-02-17 11:23:17 -08:00
Sam Edwards 70eda86484 Ensure log output is correct with used IDs 2024-02-17 11:23:17 -08:00
Sam Edwards 9d4207ea14 Only read INI per-device config once in "finalize" function, include filenames in output 2024-02-17 11:23:17 -08:00
Sam Edwards 1d49003fe1 Clean up logging in setup functions 2024-02-17 11:22:27 -08:00
Sam Edwards 0a30396c3b Make better use of macros, functions, and dynamic char array lengths 2024-02-17 11:21:26 -08:00
Sam Edwards ba06827814 Whitespace/Type cleanup 2024-02-17 11:21:26 -08:00
Eric Helgeson a45af3edec
Merge pull request #261 from WilliamLeara/fix_typographical_errors
fix typographical mistakes in comments
2024-02-17 12:05:27 -06:00
William Leara 0be37ccde0 fix typographical mistakes in comments
Also, fix up some white-space issues.  No functional change here.
2024-02-14 13:31:35 -06:00
Eric Helgeson e236b5b054
Merge pull request #252 from dotsam/easy-leds
Easier to change LEDs
2024-02-09 20:11:36 -06:00
Sam Edwards 47179c5fd0
Easier to change LEDs
- For building for boards with different LEDs and configurations
 - Define mode for LED
 - Update GPIO macros for port C
 - Use macros to generate bits for registers
2024-02-09 17:42:07 -08:00
Eric Helgeson 59c858332a dev 2023-11-16 21:39:00 -06:00
Eric Helgeson dddb7bfc1c v1.1-20231116 2023-11-16 21:38:41 -06:00
Eric Helgeson 8b79df00ba
Merge pull request #257 from erichelgeson/eric/toolbox
BlueSCSI Toolbox
2023-11-16 21:35:26 -06:00
Eric Helgeson 7c9428131b Update commands to match v2 2023-11-16 21:30:15 -06:00
Eric Helgeson e35bae560c constant for block size 2023-11-16 21:30:15 -06:00
Eric Helgeson 13d7b985b7 Reuse m_buf and allow for 100 files 2023-11-16 21:30:15 -06:00
Eric Helgeson 27b7427301 hide hidden files 2023-11-16 21:30:15 -06:00
Eric Helgeson 98162fcd84 Fix issue with large files MSB being overwritten 2023-11-16 21:30:10 -06:00
Eric Helgeson 2be810e2ca 68k app 2023-10-24 17:17:12 -05:00
Eric Helgeson 4f7c0fe2c2
Merge pull request #253 from dotsam/compiler-warnings
Fix some compiler warnings
2023-08-13 10:22:33 -05:00
Sam Edwards 458617a827
Fix some compiler warnings
- Fix syntax of a cast to a uint32
 - Remove defines that are already made by the base platform
2023-08-11 11:47:34 -07:00
Eric Helgeson 810eae9be3
Merge pull request #251 from dotsam/fix-debug
Ensure allocLength is defined when needed in onReadBuffer
2023-08-10 20:37:08 -05:00
Sam Edwards d67fa80cf0
Ensure allocLength is defined when needed in onReadBuffer 2023-08-10 16:57:06 -07:00
Eric Helgeson 9e746ba145 Back to snapshot 2023-07-08 12:06:50 -05:00
Eric Helgeson e0d115f33c v1.1-20230708 2023-07-08 11:53:38 -05:00
Eric Helgeson 27d13192b0 Ignore kicad backup dir 2023-07-08 11:45:08 -05:00
Eric Helgeson 43335afbdb Use fork of SdFat that supports gpt partitions so users do not have to format their cards with mbr. Thanks @PetteriAimonen for the patch. 2023-04-01 11:24:11 -05:00
Troy ad4737f384 INQUIRY supports small transfer sizes now
this fixes some Apple II SCSI cards like RAMFast and CMS
2023-04-01 11:23:53 -05:00
Eric Helgeson 927f8101fa Allow optical files that are set to RO on the SD card to be used 2023-04-01 11:20:19 -05:00
Eric Helgeson 142117c83a Merge remote-tracking branch 'jokker/troy/inifile' 2023-04-01 11:13:05 -05:00
Troy c5eff4f5fd megastemode renamed to maplunstoids in inifile 2022-12-28 22:16:54 -05:00
Troy c901efb929 fixed up CI/CD to work with ext libs better 2022-12-28 15:16:21 -05:00
Troy 2d0b466296 remove miniIni from PIO lib_deps 2022-12-28 11:30:07 -05:00
Eric Helgeson 075e21d981
Merge pull request #208 from erichelgeson/eric/clarifyType
Clarify the bluepill must be the C8T6 and not a C6T6
2022-12-27 16:38:11 -06:00
Eric Helgeson 71b1d3ca31 Clarify the bluepill must be the C8T6 and not a C6T6 2022-12-27 15:37:05 -06:00
Troy 5bad53008a bluescsi.ini support enabled for some features
MegaSTE Mode
Per SCSI device Product/Vendor/Revision

bunch of text refactoring for brievity and byteshaving
some reorg of the SD card info printed in the log
other little bits here and there
2022-12-22 17:45:20 -05:00
Troy c2c4b83925 miniIni lib added 2022-12-22 11:19:59 -05:00
Eric Helgeson 5506a90042 Back to snapshot 2022-12-22 08:55:46 -06:00
Eric Helgeson b117ebb39d v1.1-20221203 2022-12-03 16:14:13 -06:00
Eric Helgeson 139a510ee7
Merge pull request #201 from erichelgeson/eric/fragmented
Warn user if file is fragmented
2022-12-03 16:04:45 -06:00
Eric Helgeson 7ee2c44c6a Warn user if file is fragmented
See: https://github.com/greiman/SdFat/issues/256#issuecomment-778795554
- performance can be impacted if the file is fragmented on the SD card.
2022-12-03 15:22:07 -06:00
Eric Helgeson f4d7cdfa92
Merge pull request #196 from mynameistroy/troy/megaste_mode
MegaSTE mode added for using IDs as LUNs
2022-12-01 14:28:58 -06:00
Eric Helgeson a8903d42f2
Merge pull request #194 from mynameistroy/troy/cdb_length_fix
added a full lookup table for cdb length
2022-12-01 14:28:50 -06:00
Troy 0ae77a7b48 cleaned things up and added a log message 2022-11-30 21:42:38 -05:00
Eric Helgeson 726d72c09b
Merge pull request #186 from erichelgeson/eric/spiTake3
Revert "We only need to test 50 and 25 as the rest are just divided and rounded to full and half."
2022-11-22 16:51:21 -06:00
Eric Helgeson dc0bce859c Revert "We only need to test 50 and 25 as the rest are just divided and rounded to full and half."
This reverts commit 1cfaf00594.
2022-11-22 16:50:07 -06:00
Troy dba1860856 MegaSTE mode added for using IDs as LUNs 2022-11-13 18:08:41 -05:00
Troy 6ec75300c4 added a full lookup table for cdb length
this fixes the issue of 16 byte cdb commands
from causing problems by not being detected correctly
2022-11-06 12:20:30 -05:00
Eric Helgeson 5bb2dee71f
Merge pull request #187 from erichelgeson/eric/renameSTL
Rename file to be more clear
2022-10-14 16:20:32 -05:00
Eric Helgeson bb4b688cad Rename file to be more clear 2022-10-14 16:20:04 -05:00
Eric Helgeson 44b6270ef8
Merge pull request #172 from lausvi/main
external-db25 3D-printed case remix
2022-10-14 16:08:32 -05:00
Eric Helgeson 30cf64ff83
Merge pull request #185 from command-tab/patch-1
Fix typo in README.adoc
2022-10-14 16:06:35 -05:00
Collin Allen d3ef149aa3
Fix typo in README.adoc
Gerbers was misspelled as Grebers
2022-10-14 12:18:09 -07:00
Eric Helgeson e930ecfcde
Merge pull request #183 from mynameistroy/troy/myst_fix
Fix for incorrect response to READ_DVD_STRUCTURE
2022-10-13 14:53:50 -05:00
Troy 847456203f Cleaned up SDfs logging 2022-10-12 21:15:18 -04:00
Troy fb3a266c86 Report error for READ_DVD_STRUCTURE
fixes issues with drivers getting confused about the optical image file
2022-10-12 21:08:47 -04:00
Ville Laustela 330c3edd91 external-3D-printed-case-remix 2022-09-20 19:11:43 +03:00
Ville Laustela 57ff0cd3b4 external-3D-printed-case-remix 2022-09-20 19:07:56 +03:00
Eric Helgeson 452930940c Back to snapshot 2022-09-18 15:30:42 -05:00
Eric Helgeson 7c6f9aef75 v1.1-20220917 2022-09-17 09:53:00 -05:00
Mike a7af02057f Add apostrophe to log message 2022-09-17 09:53:00 -05:00
Eric Helgeson 19f6b428a1
Merge pull request #151 from erichelgeson/eric/longFileNames
Allow for file names to be up to 64 chars
2022-09-17 09:42:04 -05:00
Eric Helgeson b0a47bc376 Allow for file names to be up to 64 chars 2022-09-17 09:37:32 -05:00
Eric Helgeson dc9552410f
Merge pull request #130 from mynameistroy/troy/optical
CD/DVD emulation
2022-09-13 20:46:54 -05:00
Troy 67d3909dd9 Cleaned up MODE_SELECT blocksize request
OnReadBuffer uses generic buffer and only transfers actual length
2022-09-11 18:32:29 -04:00
Troy 3f279230d9 cleanup Apple Mode Page handling 2022-09-08 22:02:55 -04:00
Troy 331cd96ac5 minor logging cleanup 2022-09-06 22:46:38 -04:00
Troy 3952181d4f Clean up some remaining pinMode => gpio_mode 2022-09-06 22:45:26 -04:00
Eric Helgeson c12aa478d5
Merge pull request #161 from erichelgeson/eric/kay_stl
Add Kay's STL
2022-08-25 18:50:12 -05:00
Eric Helgeson 9831c38237 Add Kay's STL 2022-08-25 18:47:59 -05:00
Troy 94a5e711e3 MODE SELECT supports CDROM 512/2048 sector toggle
CDROM devices return a DBD properly
CDROM Error Mode Page has a default retry of 1
More streamlining of debug logging
2022-08-17 21:15:15 -04:00
Eric Helgeson 6b2152d1f7
Merge pull request #152 from erichelgeson/eric/SDFat2.2.0
Upgrade SdFat from 2.0.6 to 2.2.0
2022-08-03 09:07:23 -05:00
Eric Helgeson a06e840e39 Upgrade SdFat from 2.0.6 to 2.2.0
No change in perf
2022-07-26 18:28:19 -05:00
Troy 32453b9886 added missing set of raw sector size 2022-07-24 23:11:15 -04:00
Troy 3a3d68cf99 alternative sector size supported
this let's .BIN files get used at the very least, maybe more depending
on how some of the other optical formats look
reverted a small SCSI phase change that causes problems
2022-07-22 20:38:59 -04:00
Troy 30b794e03d merged in latest HEAD, cleaned up some warnings 2022-07-17 11:49:08 -04:00
Troy 2611ec885b out of phase error fixed, SGI Indy works now
basic determination of arbitration
2022-07-17 11:26:07 -04:00
Troy 953309be30 ISO-9660 raw image support for CD/DVD emulation 2022-07-17 11:26:05 -04:00
Troy b00dac7339 more code shuffling 2022-07-17 11:20:58 -04:00
Troy b53524e6c7 code layout adjustments 2022-07-17 11:20:55 -04:00
20 changed files with 2577 additions and 733 deletions

View File

@ -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

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
.vscode/
workspace.code-workspace
dist/
hw/*/*-backups/

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

View File

@ -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`

View File

@ -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

15
lib/minIni/minGlue.h Normal file
View 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
View 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, &quotes); /* 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, &quotes); /* 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
View 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 */

View File

@ -6,14 +6,12 @@ default_envs = STM32F1
[env]
framework = arduino
lib_deps =
greiman/SdFat @ 2.0.6
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_GENERIC_STM32F103C
-DARDUINO_LIB_DISCOVERY_PHASE
-DARDUINO=10813
-DARDUINO_ARCH_STM32
-DDEBUG_LEVEL=DEBUG_NONE
-O2
@ -66,7 +64,7 @@ board_build.mcu = stm32f103c8t6
board_build.core = maple
framework = arduino
lib_deps =
greiman/SdFat @ 2.0.6
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
@ -78,9 +76,7 @@ build_flags =
-D USB_MANUFACTURER="Unknown"
-D USB_PRODUCT="\"BLUEPILL_F103C8\""
-D HAL_PCD_MODULE_ENABLED
-DARDUINO_GENERIC_STM32F103C
-DARDUINO_LIB_DISCOVERY_PHASE
-DARDUINO=10813
-DARDUINO_ARCH_STM32
-DDEBUG_LEVEL=DEBUG_NONE
-O2

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,11 @@
#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
@ -26,6 +31,29 @@
#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)
@ -71,8 +99,6 @@
#define IO PB7 // SCSI:I/O
#define SD_CS PA4 // SDCARD:CS
#define LED PC13 // LED
#define LED2 PA0 // External LED
// Image Set Selector
#ifdef XCVR
@ -88,17 +114,29 @@
#define PBREG GPIOB->regs
#define PCREG GPIOC->regs
// LED control
#define LED_ON() PCREG->BSRR = 0b00100000000000000000000000000000; PAREG->BSRR = 0b00000000000000000000000000000001;
#define LED_OFF() PCREG->BSRR = 0b00000000000000000010000000000000; PAREG->BSRR = 0b00000000000000010000000000000000;
// 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?PBREG:PAREG)
#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
@ -113,7 +151,7 @@
// 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,avtive=1)
// 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"); }
@ -144,7 +182,7 @@
| 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)))
@ -178,7 +216,7 @@
#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() { pinMode(REQ, INPUT); pinMode(MSG, INPUT); pinMode(CD, INPUT); pinMode(IO, INPUT); pinMode(BSY, INPUT); TRANSCEIVER_IO_SET(vTR_TARGET,TR_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
@ -207,20 +245,22 @@
// 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 = 0x11111111*DB_MODE_IN; }
#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 32 // Maximum file name length
#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(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]
@ -250,16 +290,9 @@ uint32_t db_bsrr[256];
// #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)
enum SCSI_DEVICE_TYPE
{
SCSI_DEVICE_HDD,
SCSI_DEVICE_OPTICAL,
};
#define CDROM_RAW_SECTORSIZE 2352
#define CDROM_COMMON_SECTORSIZE 2048
struct SCSI_INQUIRY_DATA
typedef struct _SCSI_INQUIRY_DATA
{
union
{
@ -302,22 +335,37 @@ struct SCSI_INQUIRY_DATA
// 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
uint8_t m_type; // SCSI device type
uint32_t m_blockcount; // blockcount
bool m_raw; // Raw disk
SCSI_INQUIRY_DATA *inquiry_block; // SCSI information
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
bool m_mode2; // MODE2 CDROM
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
#endif

View File

@ -51,6 +51,14 @@
#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

View File

@ -27,9 +27,13 @@
#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
@ -39,8 +43,12 @@
#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