diff -Nru kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/addon.xml.in kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/addon.xml.in
--- kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/addon.xml.in 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/addon.xml.in 2013-05-31 22:59:22.000000000 +0000
@@ -1,7 +1,7 @@
@ADDON_DEPENDS@
@@ -12,8 +12,10 @@
tags="true"
library_@PLATFORM@="@LIBRARY_FILENAME@"/>
- NCSF Audio Decoder
- NCSF Audio Decoder
+ NCSF (Nitro Composer Sound Format) Audio Decoder
+ Adds decoding support for Nitro Composer Sound Format files (.NCSF/.MININCSF).
+
+Nitro Composer Sound Format (NCSF) is a PSF-style for Nintendo DS music, specifically music that was created via Nitro Composer, a tracker-like composer that is part of Nintendo's Nitro SDK for the Nintendo DS.
@PLATFORM@
Binary files /tmp/tmpX_cAhn/1bnZrSb2Uj/kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/icon.png and /tmp/tmpX_cAhn/5w821nS2xU/kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/icon.png differ
diff -Nru kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/resources/language/resource.language.en_gb/strings.po kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/resources/language/resource.language.en_gb/strings.po
--- kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/resources/language/resource.language.en_gb/strings.po 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/resources/language/resource.language.en_gb/strings.po 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,73 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: XBMC Main Translation Project (Frodo)\n"
+"Report-Msgid-Bugs-To: http://trac.xbmc.org/\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: XBMC Translation Team\n"
+"Language-Team: English (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/en/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "#30000"
+msgid "Default length"
+msgstr ""
+
+msgctxt "#30001"
+msgid "Used if specified file does not provide the length"
+msgstr ""
+
+msgctxt "#30002"
+msgid "unused"
+msgstr ""
+
+msgctxt "#30003"
+msgid "{0:d} s"
+msgstr ""
+
+msgctxt "#30004"
+msgid "Default fade out time"
+msgstr ""
+
+msgctxt "#30005"
+msgid "Used if specified file does not provide the length and fade"
+msgstr ""
+
+msgctxt "#30006"
+msgid "unused"
+msgstr ""
+
+msgctxt "#30007"
+msgid "{0:d} ms"
+msgstr ""
+
+msgctxt "#30008"
+msgid "Suppress opening silence"
+msgstr ""
+
+msgctxt "#30009"
+msgid "Some files start with silent data, this prevents it and looks for the beginning"
+msgstr ""
+
+msgctxt "#30010"
+msgid "Suppress end silence"
+msgstr ""
+
+msgctxt "#30011"
+msgid "Some files end with silent data, this prevents it and looks for the end"
+msgstr ""
+
+msgctxt "#30012"
+msgid "Second of silence to check"
+msgstr ""
+
+msgctxt "#30013"
+msgid "How many silent seconds are allowed before playback is stopped"
+msgstr ""
+
+msgctxt "#30014"
+msgid "{0:d} s"
+msgstr ""
diff -Nru kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/resources/settings.xml kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/resources/settings.xml
--- kodi-audiodecoder-ncsf-2.1.0/audiodecoder.ncsf/resources/settings.xml 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/audiodecoder.ncsf/resources/settings.xml 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,57 @@
+
+
+
+
+
+ 0
+
+ 0
+ 5
+ 500
+
+
+ 30003
+
+
+
+ 10000
+
+ 0
+ 100
+ 50000
+
+
+ 30007
+
+
+
+ true
+
+
+
+ true
+
+
+
+ 5
+
+ 1
+ 1
+ 20
+
+
+
+
+ true
+ true
+
+
+
+
+ 30014
+
+
+
+
+
+
diff -Nru kodi-audiodecoder-ncsf-2.1.0/CMakeLists.txt kodi-audiodecoder-ncsf-2.2.0/CMakeLists.txt
--- kodi-audiodecoder-ncsf-2.1.0/CMakeLists.txt 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/CMakeLists.txt 2013-05-31 22:59:22.000000000 +0000
@@ -4,26 +4,20 @@
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR})
find_package(Kodi REQUIRED)
-find_package(p8-platform REQUIRED)
find_package(ZLIB REQUIRED)
include_directories(${KODI_INCLUDE_DIR}/..
- ${p8-platform_INCLUDE_DIRS}
- ${PROJECT_SOURCE_DIR}/lib/psflib
- ${PROJECT_SOURCE_DIR}/lib/SSEQPlayer)
+ ${PROJECT_SOURCE_DIR}/lib)
add_subdirectory(lib/psflib)
add_subdirectory(lib/SSEQPlayer)
-set(NCSF_SOURCES src/NCSFCodec.cpp
- src/RingBuffer.cpp)
+set(NCSF_SOURCES src/NCSFCodec.cpp)
-set(NCSF_HEADERS src/RingBuffer.h)
+set(NCSF_HEADERS src/NCSFCodec.h
+ src/CircularBuffer.h)
-set(DEPLIBS ${p8-platform_LIBRARIES} psflib SSEQPlayer ${ZLIB_LIBRARIES})
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED 1)
+set(DEPLIBS psflib SSEQPlayer ${ZLIB_LIBRARIES})
build_addon(audiodecoder.ncsf NCSF DEPLIBS)
diff -Nru kodi-audiodecoder-ncsf-2.1.0/debian/changelog kodi-audiodecoder-ncsf-2.2.0/debian/changelog
--- kodi-audiodecoder-ncsf-2.1.0/debian/changelog 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/debian/changelog 2013-05-31 22:59:22.000000000 +0000
@@ -1,4 +1,4 @@
-kodi-audiodecoder-ncsf (2.1.0-1~bionic) bionic; urgency=low
+kodi-audiodecoder-ncsf (2.2.0-1~bionic) bionic; urgency=low
[ kodi ]
* autogenerated dummy changelog
diff -Nru kodi-audiodecoder-ncsf-2.1.0/debian/control kodi-audiodecoder-ncsf-2.2.0/debian/control
--- kodi-audiodecoder-ncsf-2.1.0/debian/control 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/debian/control 2013-05-31 22:59:22.000000000 +0000
@@ -2,7 +2,7 @@
Priority: extra
Maintainer: Arne Morten Kvarving
Build-Depends: debhelper (>= 8.0.0), cmake, kodi-addon-dev,
- libp8-platform-dev, zlib1g-dev
+ zlib1g-dev
Standards-Version: 3.9.6
Section: libs
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/kodi-psflib-note.txt kodi-audiodecoder-ncsf-2.2.0/lib/kodi-psflib-note.txt
--- kodi-audiodecoder-ncsf-2.1.0/lib/kodi-psflib-note.txt 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/kodi-psflib-note.txt 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,2 @@
+psflib source from https://github.com/kode54/psflib
+Sync to 6cb3515 (2 May 2019)
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/kodi-SSEQPlayer-note.txt kodi-audiodecoder-ncsf-2.2.0/lib/kodi-SSEQPlayer-note.txt
--- kodi-audiodecoder-ncsf-2.1.0/lib/kodi-SSEQPlayer-note.txt 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/kodi-SSEQPlayer-note.txt 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,2 @@
+SSEQPlayer source from https://github.com/kode54/SSEQPlayer
+Sync to 91436e7 (15 Jul 2015)
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psf2fs.c kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psf2fs.c
--- kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psf2fs.c 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psf2fs.c 2013-05-31 22:59:22.000000000 +0000
@@ -1,3 +1,27 @@
+/*
+PSFLIB - PSF2FS implementation
+
+Copyright (c) 2012-2015 Christopher Snowhill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
#include "psf2fs.h"
#include
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psf2fs.h kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psf2fs.h
--- kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psf2fs.h 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psf2fs.h 2013-05-31 22:59:22.000000000 +0000
@@ -1,3 +1,27 @@
+/*
+PSFLIB - PSF2FS implementation
+
+Copyright (c) 2012-2015 Christopher Snowhill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
#ifndef PSF2FS_H
#define PSF2FS_H
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psflib.c kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psflib.c
--- kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psflib.c 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psflib.c 2013-05-31 22:59:22.000000000 +0000
@@ -1,3 +1,27 @@
+/*
+PSFLIB - Main PSF parser implementation
+
+Copyright (c) 2012-2015 Christopher Snowhill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
#include "psflib.h"
#include
@@ -12,528 +36,726 @@
#include
#endif
+#undef strdup
#define strdup(s) my_strdup(s)
static char * my_strdup(const char * s)
{
- size_t l;
- char * r;
- if (!s) return NULL;
- l = strlen(s) + 1;
- r = (char *) malloc(l);
- if (!r) return NULL;
- memcpy(r, s, l);
- return r;
-}
-
-const char * strrpbrk( const char * s, const char * accept)
-{
- const char * start;
-
- if ( !s || !*s || !accept || !*accept ) return NULL;
-
- start = s;
- s += strlen( s ) - 1;
-
- while (s >= start)
- {
- const char *a = accept;
- while (*a != '\0')
- if (*a++ == *s)
- return s;
- --s;
- }
+ size_t l;
+ char * r;
+ if (!s) return NULL;
+ l = strlen(s) + 1;
+ r = (char *)malloc(l);
+ if (!r) return NULL;
+ memcpy(r, s, l);
+ return r;
+}
+
+const char * strrpbrk(const char * s, const char * accept)
+{
+ const char * start;
+
+ if (!s || !*s || !accept || !*accept) return NULL;
+
+ start = s;
+ s += strlen(s) - 1;
+
+ while (s >= start)
+ {
+ const char *a = accept;
+ while (*a != '\0')
+ if (*a++ == *s)
+ return s;
+ --s;
+ }
- return NULL;
+ return NULL;
}
enum { max_recursion_depth = 10 };
typedef struct psf_load_state
{
- int depth;
+ int depth;
+
+ unsigned char allowed_version;
- unsigned char allowed_version;
+ char * base_path;
+ const psf_file_callbacks * file_callbacks;
- char * base_path;
- const psf_file_callbacks * file_callbacks;
+ psf_load_callback load_target;
+ void * load_context;
- psf_load_callback load_target;
- void * load_context;
+ psf_info_callback info_target;
+ void * info_context;
+ int info_want_nested_tags;
- psf_info_callback info_target;
- void * info_context;
- int info_want_nested_tags;
+ psf_status_callback status_target;
+ void * status_context;
- char lib_name_temp[32];
+ char lib_name_temp[32];
} psf_load_state;
-static int psf_load_internal( psf_load_state * state, const char * file_name );
+static int psf_load_internal(psf_load_state * state, const char * file_name);
-int psf_load( const char * uri, const psf_file_callbacks * file_callbacks, uint8_t allowed_version,
- psf_load_callback load_target, void * load_context, psf_info_callback info_target, void * info_context, int info_want_nested_tags )
+
+static int psf_want_status(psf_load_state * state);
+static void psf_status(psf_load_state * state, const char * message, int indent);
+
+int psf_load(const char * uri, const psf_file_callbacks * file_callbacks, uint8_t allowed_version,
+ psf_load_callback load_target, void * load_context, psf_info_callback info_target,
+ void * info_context, int info_want_nested_tags, psf_status_callback status_target,
+ void * status_context)
{
- int rval;
+ int rval;
- psf_load_state state;
+ psf_load_state state;
- const char * file_name;
+ const char * file_name;
- if ( !uri || !*uri || !file_callbacks || !file_callbacks->path_separators || !*file_callbacks->path_separators || !file_callbacks->fopen ||
- !file_callbacks->fread || !file_callbacks->fseek || !file_callbacks->fclose || !file_callbacks->ftell ) return -1;
+ if (!uri || !*uri || !file_callbacks || !file_callbacks->path_separators || !*file_callbacks->path_separators || !file_callbacks->fopen ||
+ !file_callbacks->fread || !file_callbacks->fseek || !file_callbacks->fclose || !file_callbacks->ftell) return -1;
- state.depth = 0;
- state.allowed_version = allowed_version;
- state.file_callbacks = file_callbacks;
- state.load_target = load_target;
- state.load_context = load_context;
- state.info_target = info_target;
- state.info_context = info_context;
- state.info_want_nested_tags = info_want_nested_tags;
+ state.depth = 0;
+ state.allowed_version = allowed_version;
+ state.file_callbacks = file_callbacks;
+ state.load_target = load_target;
+ state.load_context = load_context;
+ state.info_target = info_target;
+ state.info_context = info_context;
+ state.info_want_nested_tags = info_want_nested_tags;
+ state.status_target = status_target;
+ state.status_context = status_context;
- state.base_path = strdup( uri );
- if ( !state.base_path ) return -1;
+ state.base_path = strdup(uri);
+ if (!state.base_path)
+ {
+ psf_status(&state, "Out of memory allocating state.base_path\n", 1);
+ return -1;
+ }
- file_name = strrpbrk( uri, file_callbacks->path_separators );
+ file_name = strrpbrk(uri, file_callbacks->path_separators);
- if ( file_name )
- {
- ++file_name;
- state.base_path[ file_name - uri ] = '\0';
- }
- else
- {
- state.base_path[ 0 ] = '\0';
- file_name = uri;
- }
+ if (file_name)
+ {
+ ++file_name;
+ state.base_path[file_name - uri] = '\0';
+ }
+ else
+ {
+ state.base_path[0] = '\0';
+ file_name = uri;
+ }
- rval = psf_load_internal( &state, file_name );
+ rval = psf_load_internal(&state, file_name);
- free( state.base_path );
+ free(state.base_path);
- return rval;
+ psf_status(&state, "Done.", 0);
+
+ return rval;
}
typedef struct psf_tag psf_tag;
struct psf_tag {
- char * name;
- char * value;
- psf_tag * next, *prev;
+ char * name;
+ char * value;
+ psf_tag * next, *prev;
};
#define FIELDS_SPLIT 6
-static const char * fields_to_split[FIELDS_SPLIT] = {"ARTIST", "ALBUM ARTIST", "PRODUCER", "COMPOSER", "PERFORMER", "GENRE"};
+static const char * fields_to_split[FIELDS_SPLIT] = { "ARTIST", "ALBUM ARTIST", "PRODUCER", "COMPOSER", "PERFORMER", "GENRE" };
-static int check_split_value( const char * name )
+static int check_split_value(const char * name)
{
- unsigned i;
- for ( i = 0; i < FIELDS_SPLIT; i++ )
- {
- if ( !strcasecmp( name, fields_to_split[ i ] ) ) return 1;
- }
- return 0;
-}
-
-static psf_tag * find_tag( psf_tag * tags, const char * name )
-{
- if ( tags && name && *name )
- {
- while ( tags )
- {
- if ( !strcasecmp( tags->name, name ) ) return tags;
- tags = tags->next;
- }
- }
-
- return NULL;
-}
-
-static void free_tags( psf_tag * tags )
-{
- psf_tag * tag = tags, * next;
- while ( tag )
- {
- next = tag->next;
- if ( tag->name ) free( tag->name );
- if ( tag->value ) free( tag->value );
- free( tag );
- tag = next;
- }
-}
-
-static psf_tag * add_tag_multi( psf_tag * tags, const char * name, const char ** values, int values_count )
-{
- psf_tag * footer;
- psf_tag * tag;
-
- int i;
-
- if ( !name || !*name || !values || !values_count || !*values ) return NULL;
-
- footer = tags;
-
- tag = find_tag( tags, name );
- if ( !tag )
- {
- tag = calloc(1, sizeof(psf_tag));
- if (!tag) return footer;
- tag->name = strdup( name );
- if ( !tag->name )
- {
- free( tag );
- return footer;
- }
- tag->next = tags;
- if ( tags ) tags->prev = tag;
- footer = tag;
- }
- if ( tag->value )
- {
- size_t old_length = strlen(tag->value);
- size_t new_length = strlen( values[ 0 ] );
- char * new_value = (char *) realloc( tag->value, old_length + new_length + 2 );
- if (!new_value) return footer;
- tag->value = new_value;
- new_value[ old_length ] = '\n';
+ unsigned i;
+ for (i = 0; i < FIELDS_SPLIT; i++)
+ {
+ if (!strcasecmp(name, fields_to_split[i])) return 1;
+ }
+ return 0;
+}
+
+static psf_tag * find_tag(psf_tag * tags, const char * name)
+{
+ if (tags && name && *name)
+ {
+ while (tags)
+ {
+ if (!strcasecmp(tags->name, name)) return tags;
+ tags = tags->next;
+ }
+ }
+
+ return NULL;
+}
+
+static void free_tags(psf_tag * tags)
+{
+ psf_tag * tag = tags, *next;
+ while (tag)
+ {
+ next = tag->next;
+ if (tag->name) free(tag->name);
+ if (tag->value) free(tag->value);
+ free(tag);
+ tag = next;
+ }
+}
+
+static psf_tag * add_tag_multi(psf_tag * tags, const char * name, const char ** values, int values_count)
+{
+ psf_tag * footer;
+ psf_tag * tag;
+
+ int i;
+
+ if (!name || !*name || !values || !values_count || !*values) return NULL;
+
+ footer = tags;
+
+ tag = find_tag(tags, name);
+ if (!tag)
+ {
+ tag = calloc(1, sizeof(psf_tag));
+ if (!tag) return footer;
+ tag->name = strdup(name);
+ if (!tag->name)
+ {
+ free(tag);
+ return footer;
+ }
+ tag->next = tags;
+ if (tags) tags->prev = tag;
+ footer = tag;
+ }
+ if (tag->value)
+ {
+ size_t old_length = strlen(tag->value);
+ size_t new_length = strlen(values[0]);
+ char * new_value = (char *)realloc(tag->value, old_length + new_length + 2);
+ if (!new_value) return footer;
+ tag->value = new_value;
+ new_value[old_length] = '\n';
#if _MSC_VER >= 1300
- strcpy_s( new_value + old_length + 1, new_length + 1, values[ 0 ] );
+ strcpy_s(new_value + old_length + 1, new_length + 1, values[0]);
#else
- strcpy( new_value + old_length + 1, values[ 0 ] );
+ strcpy(new_value + old_length + 1, values[0]);
#endif
- }
- else
- {
- tag->value = strdup( values[ 0 ] );
- if ( !tag->value ) return footer;
- }
-
- for (i = 1; i < values_count; i++)
- {
- tag = calloc(1, sizeof(psf_tag));
- if ( !tag ) return footer;
- tag->name = strdup( name );
- if ( !tag->name )
- {
- free( tag );
- return footer;
- }
- tag->value = strdup( values[ i ] );
- if ( !tag->value )
- {
- free( tag->name );
- free( tag );
- return footer;
- }
- tag->next = footer;
- if ( footer ) footer->prev = tag;
- footer = tag;
- }
-
- return footer;
-}
-
-static psf_tag * add_tag( psf_tag * tags, const char * name, const char * value )
-{
- int values_count;
- const char ** values;
- char * value_split;
-
- if ( !name || !*name || !value || !*value ) return tags;
-
- if ( check_split_value( name ) )
- {
- char * split_point, * remain;
- const char ** new_values;
- values_count = 0;
- values = NULL;
- value_split = strdup( value );
- if ( !value_split ) return tags;
- remain = value_split;
- split_point = strstr( value_split, "; " );
- while ( split_point )
- {
- values_count++;
- new_values = (const char **) realloc( (void *) values, sizeof(const char*) * ((values_count + 3) & ~3) );
- if ( !new_values )
- {
- if ( values ) free( (void *) values );
- free( value_split );
- return tags;
- }
- values = new_values;
- *split_point = '\0';
- values[ values_count - 1 ] = remain;
- remain = split_point + 2;
- split_point = strstr( remain, "; " );
- }
- if ( *remain )
- {
- values_count++;
- new_values = (const char **) realloc( (void *) values, sizeof(char*) * ((values_count + 3) & ~3) );
- if ( !new_values )
- {
- if ( values ) free( (void *) values );
- free( value_split );
- return tags;
- }
- values = new_values;
- values[ values_count - 1 ] = remain;
- }
- }
- else
- {
- values_count = 1;
- value_split = NULL;
- values = (const char **) malloc(sizeof(const char *));
- if ( !values ) return tags;
- values[ 0 ] = value;
- }
+ }
+ else
+ {
+ tag->value = strdup(values[0]);
+ if (!tag->value) return footer;
+ }
+
+ for (i = 1; i < values_count; i++)
+ {
+ tag = calloc(1, sizeof(psf_tag));
+ if (!tag) return footer;
+ tag->name = strdup(name);
+ if (!tag->name)
+ {
+ free(tag);
+ return footer;
+ }
+ tag->value = strdup(values[i]);
+ if (!tag->value)
+ {
+ free(tag->name);
+ free(tag);
+ return footer;
+ }
+ tag->next = footer;
+ if (footer) footer->prev = tag;
+ footer = tag;
+ }
+
+ return footer;
+}
+
+static psf_tag * add_tag(psf_tag * tags, const char * name, const char * value)
+{
+ int values_count;
+ const char ** values;
+ char * value_split;
+
+ if (!name || !*name || !value || !*value) return tags;
+
+ if (check_split_value(name))
+ {
+ char * split_point, *remain;
+ const char ** new_values;
+ values_count = 0;
+ values = NULL;
+ value_split = strdup(value);
+ if (!value_split) return tags;
+ remain = value_split;
+ split_point = strstr(value_split, "; ");
+ while (split_point)
+ {
+ values_count++;
+ new_values = (const char **)realloc((void *)values, sizeof(const char*) * ((values_count + 3) & ~3));
+ if (!new_values)
+ {
+ if (values) free((void *)values);
+ free(value_split);
+ return tags;
+ }
+ values = new_values;
+ *split_point = '\0';
+ values[values_count - 1] = remain;
+ remain = split_point + 2;
+ split_point = strstr(remain, "; ");
+ }
+ if (*remain)
+ {
+ values_count++;
+ new_values = (const char **)realloc((void *)values, sizeof(char*) * ((values_count + 3) & ~3));
+ if (!new_values)
+ {
+ if (values) free((void *)values);
+ free(value_split);
+ return tags;
+ }
+ values = new_values;
+ values[values_count - 1] = remain;
+ }
+ }
+ else
+ {
+ values_count = 1;
+ value_split = NULL;
+ values = (const char **)malloc(sizeof(const char *));
+ if (!values) return tags;
+ values[0] = value;
+ }
- tags = add_tag_multi( tags, name, values, values_count );
+ tags = add_tag_multi(tags, name, values, values_count);
- if ( value_split ) free( value_split );
- free( (void *) values );
+ if (value_split) free(value_split);
+ free((void *)values);
- return tags;
+ return tags;
}
/* Split line on first equals sign, and remove any whitespace surrounding the name and value fields */
-static psf_tag * process_tag_line( psf_tag * tags, char * line )
+static psf_tag * process_tag_line(psf_tag * tags, char * line)
{
- char * name, * value, * end;
- char * equals = strchr( line, '=' );
- if ( !equals ) return tags;
+ char * name, *value, *end;
+ char * equals = strchr(line, '=');
+ if (!equals) return tags;
- name = line;
- value = equals + 1;
- end = line + strlen( line );
+ name = line;
+ value = equals + 1;
+ end = line + strlen(line);
- while ( name < equals && *name > 0 && *name <= ' ' ) name++;
- if ( name == equals ) return tags;
+ while (name < equals && *name > 0 && *name <= ' ') name++;
+ if (name == equals) return tags;
- --equals;
- while ( equals > name && *equals > 0 && *equals <= ' ' ) --equals;
- equals[1] = '\0';
+ --equals;
+ while (equals > name && *equals > 0 && *equals <= ' ') --equals;
+ equals[1] = '\0';
- while ( value < end && *value > 0 && *value <= ' ' ) value++;
- if ( value == end ) return tags;
+ while (value < end && *value > 0 && *value <= ' ') value++;
+ if (value == end) return tags;
- --end;
- while ( end > value && *value > 0 && *value <= ' ' ) --end;
- end[1] = '\0';
+ --end;
+ while (end > value && *end > 0 && *end <= ' ') --end;
+ end[1] = '\0';
- if ( *name == '_' )
- {
- psf_tag * tag = find_tag( tags, name );
- if ( tag ) return tags;
- }
+ if (*name == '_')
+ {
+ psf_tag * tag = find_tag(tags, name);
+ if (tag) return tags;
+ }
- return add_tag( tags, name, value );
+ return add_tag(tags, name, value);
}
-static psf_tag * process_tags( char * buffer )
+static psf_tag * process_tags(char * buffer)
{
- psf_tag * tags = NULL;
- char * line_end;
- if ( !buffer || !*buffer ) return NULL;
+ psf_tag * tags = NULL;
+ char * line_end;
+ if (!buffer || !*buffer) return NULL;
- line_end = strpbrk( buffer, "\n\r" );
- while ( line_end )
- {
- *line_end++ = '\0';
- tags = process_tag_line( tags, buffer );
- while ( *line_end && ( *line_end == '\n' || *line_end == '\r' ) ) line_end++;
- buffer = line_end;
- line_end = strpbrk( buffer, "\n\r" );
- }
- if ( *buffer ) tags = process_tag_line( tags, buffer );
+ line_end = strpbrk(buffer, "\n\r");
+ while (line_end)
+ {
+ *line_end++ = '\0';
+ tags = process_tag_line(tags, buffer);
+ while (*line_end && (*line_end == '\n' || *line_end == '\r')) line_end++;
+ buffer = line_end;
+ line_end = strpbrk(buffer, "\n\r");
+ }
+ if (*buffer) tags = process_tag_line(tags, buffer);
- return tags;
+ return tags;
}
-static int psf_load_internal( psf_load_state * state, const char * file_name )
+static int psf_load_internal(psf_load_state * state, const char * file_name)
{
- psf_tag * tags = NULL;
- psf_tag * tag;
+ psf_tag * tags = NULL;
+ psf_tag * tag;
- char * full_path;
+ char * full_path;
- void * file;
+ void * file;
- long file_size, tag_size;
+ long file_size, tag_size;
- int n;
+ int n;
- uint8_t header_buffer[16];
+ uint8_t header_buffer[16];
- uint8_t * exe_compressed_buffer = NULL;
- uint8_t * exe_decompressed_buffer = NULL;
- uint8_t * reserved_buffer = NULL;
- char * tag_buffer = NULL;
+ uint8_t * exe_compressed_buffer = NULL;
+ uint8_t * exe_decompressed_buffer = NULL;
+ uint8_t * reserved_buffer = NULL;
+ char * tag_buffer = NULL;
- uint32_t exe_compressed_size, exe_crc32, reserved_size;
- uLong exe_decompressed_size, try_exe_decompressed_size;
+ uint32_t exe_compressed_size, exe_crc32, reserved_size;
+ uLong exe_decompressed_size, try_exe_decompressed_size;
- int zerr;
+ int zerr;
- size_t full_path_size;
+ size_t full_path_size;
- if ( ++state->depth > max_recursion_depth ) return -1;
+ if (++state->depth > max_recursion_depth)
+ {
+ psf_status(state, "Exceeded maximum file nesting depth.\n", 1);
+ return -1;
+ }
- full_path_size = strlen(state->base_path) + strlen(file_name) + 1;
- full_path = (char *) malloc( full_path_size );
- if ( !full_path ) return -1;
+ full_path_size = strlen(state->base_path) + strlen(file_name) + 1;
+ full_path = (char *)malloc(full_path_size);
+ if (!full_path) return -1;
#if _MSC_VER >= 1300
- strcpy_s( full_path, full_path_size, state->base_path );
- strcat_s( full_path, full_path_size, file_name );
+ strcpy_s(full_path, full_path_size, state->base_path);
+ strcat_s(full_path, full_path_size, file_name);
#else
- strcpy( full_path, state->base_path );
- strcat( full_path, file_name );
+ strcpy(full_path, state->base_path);
+ strcat(full_path, file_name);
#endif
- file = state->file_callbacks->fopen( full_path );
-
- free( full_path );
+ file = state->file_callbacks->fopen(full_path);
- if ( !file ) return -1;
+ free(full_path);
- if ( state->file_callbacks->fread( header_buffer, 1, 16, file ) < 16 ) goto error_close_file;
-
- if ( memcmp( header_buffer, "PSF", 3 ) ) goto error_close_file;
-
- if ( state->allowed_version && ( header_buffer[ 3 ] != state->allowed_version ) ) goto error_close_file;
-
- reserved_size = header_buffer[ 4 ] | ( header_buffer[ 5 ] << 8 ) | ( header_buffer[ 6 ] << 16 ) | ( header_buffer[ 7 ] << 24 );
- exe_compressed_size = header_buffer[ 8 ] | ( header_buffer[ 9 ] << 8 ) | ( header_buffer[ 10 ] << 16 ) | ( header_buffer[ 11 ] << 24 );
- exe_crc32 = header_buffer[ 12 ] | ( header_buffer[ 13 ] << 8 ) | ( header_buffer[ 14 ] << 16 ) | ( header_buffer[ 15 ] << 24 );
-
- if ( state->file_callbacks->fseek( file, 0, SEEK_END ) ) goto error_close_file;
-
- file_size = state->file_callbacks->ftell( file );
-
- if ( file_size <= 0 ) goto error_close_file;
-
- if ( (unsigned long)file_size >= 16 + reserved_size + exe_compressed_size + 5 )
- {
- tag_size = file_size - ( 16 + reserved_size + exe_compressed_size );
- if ( state->file_callbacks->fseek( file, -tag_size, SEEK_CUR ) ) goto error_close_file;
- tag_buffer = (char *) malloc( tag_size + 1 );
- if ( !tag_buffer ) goto error_close_file;
- if ( state->file_callbacks->fread( tag_buffer, 1, tag_size, file ) < (size_t)tag_size ) goto error_free_buffers;
- tag_buffer[ tag_size ] = 0;
- if ( !memcmp( tag_buffer, "[TAG]", 5 ) ) tags = process_tags( tag_buffer + 5 );
- free( tag_buffer );
- tag_buffer = NULL;
-
- if ( tags && state->info_target && ( state->depth == 1 || state->info_want_nested_tags ) )
- {
- tag = tags;
- while ( tag->next ) tag = tag->next;
- while ( tag )
- {
- state->info_target( state->info_context, tag->name, tag->value );
- tag = tag->prev;
- }
- }
- }
-
- if ( !state->load_target ) goto done;
-
- tag = find_tag( tags, "_lib" );
- if ( tag )
- {
- if ( psf_load_internal( state, tag->value ) < 0 ) goto error_free_tags;
- }
-
- reserved_buffer = (uint8_t *) malloc( reserved_size );
- if ( !reserved_buffer ) goto error_free_tags;
- exe_compressed_buffer = (uint8_t *) malloc( exe_compressed_size );
- if ( !exe_compressed_buffer ) goto error_free_tags;
-
- if ( state->file_callbacks->fseek( file, 16, SEEK_SET ) ) goto error_free_tags;
- if ( reserved_size && state->file_callbacks->fread( reserved_buffer, 1, reserved_size, file ) < reserved_size ) goto error_free_tags;
- if ( exe_compressed_size && state->file_callbacks->fread( exe_compressed_buffer, 1, exe_compressed_size, file ) < exe_compressed_size ) goto error_free_tags;
- state->file_callbacks->fclose( file );
- file = NULL;
-
- if ( exe_compressed_size )
- {
- if ( exe_crc32 != crc32(crc32(0L, Z_NULL, 0), exe_compressed_buffer, exe_compressed_size) ) goto error_free_tags;
-
- exe_decompressed_size = try_exe_decompressed_size = exe_compressed_size * 3;
- exe_decompressed_buffer = (uint8_t *) malloc( exe_decompressed_size );
- if ( !exe_decompressed_buffer ) goto error_free_tags;
-
- while ( Z_OK != ( zerr = uncompress( exe_decompressed_buffer, &exe_decompressed_size, exe_compressed_buffer, exe_compressed_size ) ) )
- {
- void * try_exe_decompressed_buffer;
-
- if ( Z_MEM_ERROR != zerr && Z_BUF_ERROR != zerr ) goto error_free_tags;
-
- if ( try_exe_decompressed_size < 1 * 1024 * 1024 )
- try_exe_decompressed_size += 1 * 1024 * 1024;
- else
- try_exe_decompressed_size += try_exe_decompressed_size;
-
- exe_decompressed_size = try_exe_decompressed_size;
-
- try_exe_decompressed_buffer = realloc( exe_decompressed_buffer, exe_decompressed_size );
- if ( !try_exe_decompressed_buffer ) goto error_free_tags;
-
- exe_decompressed_buffer = (uint8_t *) try_exe_decompressed_buffer;
- }
- }
- else
- {
- exe_decompressed_size = 0;
- exe_decompressed_buffer = (uint8_t *) malloc( exe_decompressed_size );
- if ( !exe_decompressed_buffer ) goto error_free_tags;
- }
-
- free( exe_compressed_buffer );
- exe_compressed_buffer = NULL;
-
- if ( state->load_target( state->load_context, exe_decompressed_buffer, exe_decompressed_size, reserved_buffer, reserved_size ) ) goto error_free_tags;
-
- free( reserved_buffer );
- reserved_buffer = NULL;
-
- free( exe_decompressed_buffer );
- exe_decompressed_buffer = NULL;
-
- n = 2;
- snprintf( state->lib_name_temp, 31, "_lib%u", n );
- state->lib_name_temp[ 31 ] = '\0';
- tag = find_tag( tags, state->lib_name_temp );
- while ( tag )
- {
- if ( psf_load_internal( state, tag->value ) < 0 ) goto error_free_tags;
- ++n;
- snprintf( state->lib_name_temp, 31, "_lib%u", n );
- state->lib_name_temp[ 31 ] = '\0';
- tag = find_tag( tags, state->lib_name_temp );
- }
+ if (!file)
+ {
+ if (psf_want_status(state))
+ {
+ psf_status(state, "Error opening file: ", 1);
+ psf_status(state, file_name, 0);
+ psf_status(state, "\n", 0);
+ psf_status(state, "From base path: ", 1);
+ psf_status(state, state->base_path, 0);
+ psf_status(state, "\n", 0);
+ }
+ return -1;
+ }
+
+ if (psf_want_status(state))
+ {
+ psf_status(state, "Opened file: ", 1);
+ psf_status(state, file_name, 0);
+ psf_status(state, "\n", 0);
+ psf_status(state, "From base path: ", 1);
+ psf_status(state, state->base_path, 0);
+ psf_status(state, "\n", 0);
+ }
+
+ if (state->file_callbacks->fread(header_buffer, 1, 16, file) < 16)
+ {
+ psf_status(state, "File too small to contain a valid header.\n", 1);
+ goto error_close_file;
+ }
+
+ if (memcmp(header_buffer, "PSF", 3))
+ {
+ psf_status(state, "File does not contain a valid PSF signature.\n", 1);
+ goto error_close_file;
+ }
+
+ if (state->allowed_version && (header_buffer[3] != state->allowed_version))
+ {
+ if (psf_want_status(state))
+ {
+ char *end;
+ char temp[8];
+ psf_status(state, "Expected PSF version ", 1);
+ snprintf(temp, 7, "%d", (int)state->allowed_version);
+ temp[7] = '\0';
+ psf_status(state, temp, 0);
+ psf_status(state, ", got ", 0);
+ snprintf(temp, 7, "%d", (int)header_buffer[3]);
+ temp[7] = '\0';
+ psf_status(state, temp, 0);
+ psf_status(state, "\n", 0);
+ }
+ goto error_close_file;
+ }
+
+ reserved_size = header_buffer[4] | (header_buffer[5] << 8) | (header_buffer[6] << 16) | (header_buffer[7] << 24);
+ exe_compressed_size = header_buffer[8] | (header_buffer[9] << 8) | (header_buffer[10] << 16) | (header_buffer[11] << 24);
+ exe_crc32 = header_buffer[12] | (header_buffer[13] << 8) | (header_buffer[14] << 16) | (header_buffer[15] << 24);
+
+ if (state->file_callbacks->fseek(file, 0, SEEK_END))
+ {
+ psf_status(state, "Could not seek to end of file to determine file size.\n", 1);
+ goto error_close_file;
+ }
+
+ file_size = state->file_callbacks->ftell(file);
+
+ if (file_size <= 0)
+ {
+ psf_status(state, "Could not determine file size.\n", 1);
+ goto error_close_file;
+ }
+
+ if ((unsigned long)file_size >= 16 + reserved_size + exe_compressed_size + 5)
+ {
+ psf_status(state, "Tag detected, attempting to read it.\n", 1);
+
+ tag_size = file_size - (16 + reserved_size + exe_compressed_size);
+ if (state->file_callbacks->fseek(file, -tag_size, SEEK_CUR))
+ {
+ psf_status(state, "Could not seek back to read tag.\n", 1);
+ goto error_close_file;
+ }
+ tag_buffer = (char *)malloc(tag_size + 1);
+ if (!tag_buffer)
+ {
+ psf_status(state, "Out of memory allocating tag buffer.\n", 1);
+ goto error_close_file;
+ }
+ if (state->file_callbacks->fread(tag_buffer, 1, tag_size, file) < (size_t)tag_size)
+ {
+ psf_status(state, "Could not read tag.\n", 1);
+ goto error_free_buffers;
+ }
+ tag_buffer[tag_size] = 0;
+ if (!memcmp(tag_buffer, "[TAG]", 5)) tags = process_tags(tag_buffer + 5);
+ free(tag_buffer);
+ tag_buffer = NULL;
+
+ if (tags && state->info_target && (state->depth == 1 || state->info_want_nested_tags))
+ {
+ tag = tags;
+ while (tag->next) tag = tag->next;
+ while (tag)
+ {
+ if (state->info_target(state->info_context, tag->name, tag->value))
+ {
+ if (psf_want_status(state))
+ {
+ psf_status(state, "Caller rejected tag: ", 1);
+ psf_status(state, tag->name, 0);
+ psf_status(state, "=", 0);
+ psf_status(state, tag->value, 0);
+ psf_status(state, "\n", 0);
+ }
+ goto error_free_tags;
+ }
+ tag = tag->prev;
+ }
+ }
+ }
+
+ if (!state->load_target) goto done;
+
+ tag = find_tag(tags, "_lib");
+ if (tag)
+ {
+ if (psf_want_status(state))
+ {
+ psf_status(state, "Found _lib: ", 1);
+ psf_status(state, tag->value, 0);
+ psf_status(state, "\n", 0);
+ }
+ if (psf_load_internal(state, tag->value) < 0) goto error_free_tags;
+ }
+
+ reserved_buffer = (uint8_t *)malloc(reserved_size);
+ if (!reserved_buffer)
+ {
+ psf_status(state, "Out of memory allocating buffer for reserved section.\n", 1);
+ goto error_free_tags;
+ }
+ exe_compressed_buffer = (uint8_t *)malloc(exe_compressed_size);
+ if (!exe_compressed_buffer)
+ {
+ psf_status(state, "Out of memory allocating buffer for compressed exe section.\n", 1);
+ goto error_free_tags;
+ }
+
+ if (state->file_callbacks->fseek(file, 16, SEEK_SET))
+ {
+ psf_status(state, "Could not seek back to main data section of file.", 1);
+ goto error_free_tags;
+ }
+ if (reserved_size && state->file_callbacks->fread(reserved_buffer, 1, reserved_size, file) < reserved_size)
+ {
+ psf_status(state, "Could not read reserved section.\n", 1);
+ goto error_free_tags;
+ }
+ if (exe_compressed_size && state->file_callbacks->fread(exe_compressed_buffer, 1, exe_compressed_size, file) < exe_compressed_size)
+ {
+ psf_status(state, "Could not read compressed exe section.\n", 1);
+ goto error_free_tags;
+ }
+ state->file_callbacks->fclose(file);
+ file = NULL;
+
+ psf_status(state, "File closed.\n", 1);
+
+ if (exe_compressed_size)
+ {
+ uint32_t got_crc32 = crc32(crc32(0L, Z_NULL, 0), exe_compressed_buffer, exe_compressed_size);
+ if (exe_crc32 != got_crc32)
+ {
+ if (psf_want_status(state))
+ {
+ char temp[16];
+ psf_status(state, "CRC mismatch on compressed exe section.\nWanted: 0x", 1);
+ snprintf(temp, 15, "%X", exe_crc32);
+ temp[15] = '\0';
+ psf_status(state, temp, 0);
+ psf_status(state, ", got 0x", 0);
+ snprintf(temp, 15, "%X", got_crc32);
+ temp[15] = '\0';
+ psf_status(state, temp, 0);
+ psf_status(state, "\n", 0);
+ }
+ goto error_free_tags;
+ }
+
+ exe_decompressed_size = try_exe_decompressed_size = exe_compressed_size * 3;
+ exe_decompressed_buffer = (uint8_t *)malloc(exe_decompressed_size);
+ if (!exe_decompressed_buffer)
+ {
+ psf_status(state, "Out of memory allocating buffer for decompressed exe section.\n", 1);
+ goto error_free_tags;
+ }
+
+ while (Z_OK != (zerr = uncompress(exe_decompressed_buffer, &exe_decompressed_size, exe_compressed_buffer, exe_compressed_size)))
+ {
+ void * try_exe_decompressed_buffer;
+
+ if (Z_MEM_ERROR != zerr && Z_BUF_ERROR != zerr)
+ {
+ psf_status(state, "Could not decompress exe section.\n", 1);
+ goto error_free_tags;
+ }
+
+ if (try_exe_decompressed_size < 1 * 1024 * 1024)
+ try_exe_decompressed_size += 1 * 1024 * 1024;
+ else
+ try_exe_decompressed_size += try_exe_decompressed_size;
+
+ exe_decompressed_size = try_exe_decompressed_size;
+
+ try_exe_decompressed_buffer = realloc(exe_decompressed_buffer, exe_decompressed_size);
+ if (!try_exe_decompressed_buffer)
+ {
+ psf_status(state, "Out of memory reallocating buffer for decompressed exe section.\n", 1);
+ goto error_free_tags;
+ }
+
+ exe_decompressed_buffer = (uint8_t *)try_exe_decompressed_buffer;
+ }
+ }
+ else
+ {
+ exe_decompressed_size = 0;
+ exe_decompressed_buffer = (uint8_t *)malloc(exe_decompressed_size);
+ if (!exe_decompressed_buffer)
+ {
+ psf_status(state, "Out of memory allocating dummy buffer for exe section.\n", 1);
+ goto error_free_tags;
+ }
+ }
+
+ free(exe_compressed_buffer);
+ exe_compressed_buffer = NULL;
+
+ psf_status(state, "Passing exe and reserved back out.\n", 1);
+
+ if (state->load_target(state->load_context, exe_decompressed_buffer, exe_decompressed_size, reserved_buffer, reserved_size))
+ {
+ psf_status(state, "Data handler returned an error.\n", 1);
+ goto error_free_tags;
+ }
+
+ free(reserved_buffer);
+ reserved_buffer = NULL;
+
+ free(exe_decompressed_buffer);
+ exe_decompressed_buffer = NULL;
+
+ n = 2;
+ snprintf(state->lib_name_temp, 31, "_lib%u", n);
+ state->lib_name_temp[31] = '\0';
+ tag = find_tag(tags, state->lib_name_temp);
+ while (tag)
+ {
+ if (psf_want_status(state))
+ {
+ psf_status(state, "Found ", 1);
+ psf_status(state, tag->name, 0);
+ psf_status(state, ": ", 0);
+ psf_status(state, tag->value, 0);
+ psf_status(state, "\n", 0);
+ }
+ if (psf_load_internal(state, tag->value) < 0) goto error_free_tags;
+ ++n;
+ snprintf(state->lib_name_temp, 31, "_lib%u", n);
+ state->lib_name_temp[31] = '\0';
+ tag = find_tag(tags, state->lib_name_temp);
+ }
done:
- if ( file ) state->file_callbacks->fclose( file );
+ if (file) state->file_callbacks->fclose(file);
- free_tags( tags );
+ free_tags(tags);
- --state->depth;
+ --state->depth;
- return header_buffer[ 3 ];
+ return header_buffer[3];
error_free_tags:
- free_tags( tags );
+ free_tags(tags);
error_free_buffers:
- if ( exe_compressed_buffer ) free( exe_compressed_buffer );
- if ( exe_decompressed_buffer ) free( exe_decompressed_buffer );
- if ( reserved_buffer ) free( reserved_buffer );
- if ( tag_buffer ) free( tag_buffer );
+ if (exe_compressed_buffer) free(exe_compressed_buffer);
+ if (exe_decompressed_buffer) free(exe_decompressed_buffer);
+ if (reserved_buffer) free(reserved_buffer);
+ if (tag_buffer) free(tag_buffer);
error_close_file:
- if ( file ) state->file_callbacks->fclose( file );
- return -1;
+ if (file) state->file_callbacks->fclose(file);
+ return -1;
+}
+
+int psf_want_status(psf_load_state * state)
+{
+ return !!state->status_target;
+}
+
+void psf_status(psf_load_state * state, const char * message, int indent)
+{
+ if (state->status_target)
+ {
+ if (indent)
+ {
+ char indent[16];
+ int indent_level = state->depth > 1 ? state->depth - 1 : 0;
+ memset(indent, ' ', indent_level);
+ indent[indent_level] = '\0';
+ state->status_target(state->status_context, indent);
+ }
+ state->status_target(state->status_context, message);
+ }
}
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psflib.h kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psflib.h
--- kodi-audiodecoder-ncsf-2.1.0/lib/psflib/psflib.h 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/psflib/psflib.h 2013-05-31 22:59:22.000000000 +0000
@@ -1,3 +1,27 @@
+/*
+PSFLIB - Main PSF parser implementation
+
+Copyright (c) 2012-2015 Christopher Snowhill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
#ifndef PSFLIB_H
#define PSFLIB_H
@@ -14,11 +38,12 @@
/* list of characters which act as path separators, null terminated */
const char * path_separators;
- /* accepts UTF-8 encoding, returns file handle */
- void * (* fopen )(const char *);
+ /* opens the file pointed to by path read-only in binary mode,
+ * accepts UTF-8 encoding, returns file handle */
+ void * (* fopen )(const char * path);
/* reads to specified buffer, returns count of size bytes read */
- size_t (* fread )(void *, size_t size, size_t count, void * handle);
+ size_t (* fread )(void * buffer, size_t size, size_t count, void * handle);
/* returns zero on success, -1 on error */
int (* fseek )(void * handle, int64_t, int);
@@ -50,6 +75,9 @@
*/
typedef int (* psf_info_callback)(void * context, const char * name, const char * value);
+/* Receives any status messages, which should be appended to one big message. */
+typedef void (* psf_status_callback)(void * context, const char * message);
+
/* Loads the PSF chain starting with uri, opened using file_callbacks, passes the tags,
* if any, to the optional info_target callback, then passes all loaded data to load_target
* with the highest priority file first.
@@ -62,7 +90,9 @@
* Returns negative on error, PSF version on success.
*/
int psf_load( const char * uri, const psf_file_callbacks * file_callbacks, uint8_t allowed_version,
- psf_load_callback load_target, void * load_context, psf_info_callback info_target, void * info_context, int info_want_nested_tags );
+ psf_load_callback load_target, void * load_context, psf_info_callback info_target,
+ void * info_context, int info_want_nested_tags, psf_status_callback status_target,
+ void * status_context);
#ifdef __cplusplus
}
diff -Nru kodi-audiodecoder-ncsf-2.1.0/lib/SSEQPlayer/LICENSE.TXT kodi-audiodecoder-ncsf-2.2.0/lib/SSEQPlayer/LICENSE.TXT
--- kodi-audiodecoder-ncsf-2.1.0/lib/SSEQPlayer/LICENSE.TXT 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/lib/SSEQPlayer/LICENSE.TXT 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,13 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
diff -Nru kodi-audiodecoder-ncsf-2.1.0/src/CircularBuffer.h kodi-audiodecoder-ncsf-2.2.0/src/CircularBuffer.h
--- kodi-audiodecoder-ncsf-2.1.0/src/CircularBuffer.h 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/src/CircularBuffer.h 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 Team Kodi
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+int const silence_threshold = 8;
+
+template
+class circular_buffer
+{
+public:
+ circular_buffer( unsigned p_size ) : readptr( 0 ), writeptr( 0 ), size( p_size ), used( 0 )
+ {
+ buffer.resize( p_size );
+ }
+ unsigned data_available() { return used; }
+ unsigned free_space() { return size - used; }
+ bool write( const T * src, unsigned count )
+ {
+ if ( count > free_space() ) return false;
+ while( count )
+ {
+ unsigned delta = size - writeptr;
+ if ( delta > count ) delta = count;
+ std::copy( src, src + delta, buffer.begin() + writeptr );
+ used += delta;
+ writeptr = ( writeptr + delta ) % size;
+ src += delta;
+ count -= delta;
+ }
+ return true;
+ }
+ unsigned read( T * dst, unsigned count )
+ {
+ unsigned done = 0;
+ for(;;)
+ {
+ unsigned delta = size - readptr;
+ if ( delta > used ) delta = used;
+ if ( delta > count ) delta = count;
+ if ( !delta ) break;
+
+ std::copy( buffer.begin() + readptr, buffer.begin() + readptr + delta, dst );
+ dst += delta;
+ done += delta;
+ readptr = ( readptr + delta ) % size;
+ count -= delta;
+ used -= delta;
+ }
+ return done;
+ }
+ void reset()
+ {
+ readptr = writeptr = used = 0;
+ }
+ void resize(unsigned p_size)
+ {
+ size = p_size;
+ buffer.resize( p_size );
+ reset();
+ }
+ bool test_silence() const
+ {
+ T* begin = (T*) &buffer[0];
+ T first = *begin;
+ *begin = silence_threshold * 2;
+ T* p = begin + size;
+ while ( (unsigned) ( *--p + silence_threshold ) <= (unsigned) silence_threshold * 2 ) { }
+ *begin = first;
+ return p == begin && ( (unsigned) ( first + silence_threshold ) <= (unsigned) silence_threshold * 2 );
+ }
+
+private:
+ std::vector buffer;
+ unsigned readptr, writeptr, used, size;
+};
diff -Nru kodi-audiodecoder-ncsf-2.1.0/src/NCSFCodec.cpp kodi-audiodecoder-ncsf-2.2.0/src/NCSFCodec.cpp
--- kodi-audiodecoder-ncsf-2.1.0/src/NCSFCodec.cpp 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/src/NCSFCodec.cpp 2013-05-31 22:59:22.000000000 +0000
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2019 Team Kodi
* Copyright (C) 2014 Arne Morten Kvarving
*
* This Program is free software; you can redistribute it and/or modify
@@ -17,21 +18,20 @@
*
*/
-#include
+#include "NCSFCodec.h"
+
#include
-#include "RingBuffer.h"
#include
#include
-#include "SDAT.h"
-#include "Player.h"
-extern "C" {
+extern "C"
+{
#include
#include
-#include "psflib.h"
+#include "psflib/psflib.h"
-static void * psf_file_fopen( const char * uri )
+static void* psf_file_fopen(const char* uri)
{
kodi::vfs::CFile* file = new kodi::vfs::CFile;
if (!file->OpenFile(uri, 0))
@@ -43,26 +43,26 @@
return file;
}
-static size_t psf_file_fread( void * buffer, size_t size, size_t count, void * handle )
+static size_t psf_file_fread(void* buffer, size_t size, size_t count, void* handle)
{
kodi::vfs::CFile* file = static_cast(handle);
return file->Read(buffer, size*count);
}
-static int psf_file_fseek( void * handle, int64_t offset, int whence )
+static int psf_file_fseek(void* handle, int64_t offset, int whence)
{
kodi::vfs::CFile* file = static_cast(handle);
return file->Seek(offset, whence) > -1 ? 0 : -1;
}
-static int psf_file_fclose( void * handle )
+static int psf_file_fclose(void* handle)
{
delete static_cast(handle);
return 0;
}
-static long psf_file_ftell( void * handle )
+static long psf_file_ftell(void* handle)
{
kodi::vfs::CFile* file = static_cast(handle);
return file->GetPosition();
@@ -78,298 +78,472 @@
psf_file_ftell
};
+inline unsigned get_le32(void const* p)
+{
+ return (unsigned) ((unsigned char const*) p) [3] << 24 |
+ (unsigned) ((unsigned char const*) p) [2] << 16 |
+ (unsigned) ((unsigned char const*) p) [1] << 8 |
+ (unsigned) ((unsigned char const*) p) [0];
}
-struct ncsf_loader_state
+} // extern "C"
+
+#define BORK_TIME 0xC0CAC01A
+static unsigned long parse_time_crap(const char* input)
{
- uint32_t sseq;
- std::vector sdatData;
- std::unique_ptr sdat;
+ unsigned long value = 0;
+ unsigned long multiplier = 1000;
+ const char* ptr = input;
+ unsigned long colon_count = 0;
+
+ while (*ptr && ((*ptr >= '0' && *ptr <= '9') || *ptr == ':'))
+ {
+ colon_count += *ptr == ':';
+ ++ptr;
+ }
+ if (colon_count > 2)
+ return BORK_TIME;
+ if (*ptr && *ptr != '.' && *ptr != ',')
+ return BORK_TIME;
+ if (*ptr)
+ ++ptr;
+ while (*ptr && *ptr >= '0' && *ptr <= '9')
+ ++ptr;
+ if (*ptr)
+ return BORK_TIME;
+
+ ptr = strrchr(input, ':');
+ if (!ptr)
+ ptr = input;
+ for (;;)
+ {
+ char* end;
+ if (ptr != input) ++ptr;
+ if (multiplier == 1000)
+ {
+ double temp = std::stod(ptr);
+ if (temp >= 60.0)
+ return BORK_TIME;
+ value = (long)(temp * 1000.0f);
+ }
+ else
+ {
+ unsigned long temp = strtoul(ptr, &end, 10);
+ if (temp >= 60 && multiplier < 3600000)
+ return BORK_TIME;
+ value += temp * multiplier;
+ }
+ if (ptr == input)
+ break;
+ ptr -= 2;
+ while (ptr > input && *ptr != ':')
+ --ptr;
+ multiplier *= 60;
+ }
- ncsf_loader_state() : sseq( 0 ) { }
-};
+ return value;
+}
+
+//------------------------------------------------------------------------------
-struct NCSFContext
+bool CNCSFCodec::Init(const std::string& filename, unsigned int filecache,
+ int& channels, int& samplerate,
+ int& bitspersample, int64_t& totaltime,
+ int& bitrate, AEDataFormat& format,
+ std::vector& channellist)
{
- ncsf_loader_state sseq;
- Player player;
- int64_t length;
- int sample_rate;
- int64_t pos;
- int year;
- std::string file;
- CRingBuffer sample_buffer;
- std::string title;
- std::string album;
-};
+ m_file = filename;
+
+ NCSFContext ctx;
+ int ret = psf_load(m_file.c_str(), &psf_file_system, 0x25,
+ nullptr, nullptr,
+ NCFSInfoMeta, &ctx, 0,
+ NCFSPrintMessage, this);
+ if (ret <= 0)
+ {
+ kodi::Log(ADDON_LOG_ERROR, "%s: Not an NCSF file (%s)", __func__, m_file.c_str());
+ return false;
+ }
+ m_tagSongMs = ctx.tagSongMs;
+ m_tagFadeMs = ctx.tagFadeMs;
-#define BORK_TIME 0xC0CAC01A
-static unsigned long parse_time_crap(const char *input)
+ kodi::CheckSettingBoolean("suppressopeningsilence", m_cfgSuppressOpeningSilence);
+ kodi::CheckSettingBoolean("suppressendsilence", m_cfgSuppressEndSilence);
+ kodi::CheckSettingInt("endsilenceseconds", m_cfgEndSilenceSeconds);
+
+ if (!m_tagSongMs)
+ {
+ m_tagSongMs = kodi::GetSettingInt("defaultlength") * 1000;
+ m_tagFadeMs = kodi::GetSettingInt("defaultfade");
+ }
+
+ if (!Load())
+ return false;
+
+ totaltime = m_tagSongMs;
+
+ format = AE_FMT_S16NE;
+ channellist = { AE_CH_FL, AE_CH_FR };
+ channels = 2;
+ bitspersample = 16;
+ bitrate = 0.0;
+ samplerate = m_cfgDefaultSampleRate;
+
+ return true;
+}
+
+bool CNCSFCodec::Load()
{
- if (!input) return BORK_TIME;
- int len = strlen(input);
- if (!len) return BORK_TIME;
- int value = 0;
+ if (m_sseq.sdatData.empty())
{
- int i;
- for (i = len - 1; i >= 0; i--)
+ int ret = psf_load(m_file.c_str(), &psf_file_system, 0x25,
+ NCSFLoader, &m_sseq,
+ nullptr, nullptr, 0,
+ NCFSPrintMessage, this);
+ if (ret <= 0)
{
- if ((input[i] < '0' || input[i] > '9') && input[i] != ':' && input[i] != ',' && input[i] != '.')
- {
- return BORK_TIME;
- }
+ kodi::Log(ADDON_LOG_ERROR, "%s: Not an NCSF file (%s)", __func__, m_file.c_str());
+ return false;
}
}
- std::string foo = input;
- char *bar = (char *) &foo[0];
- char *strs = bar + foo.size() - 1;
- while (strs > bar && (*strs >= '0' && *strs <= '9'))
- {
- strs--;
- }
- if (*strs == '.' || *strs == ',')
- {
- // fraction of a second
- strs++;
- if (strlen(strs) > 3) strs[3] = 0;
- value = atoi(strs);
- switch (strlen(strs))
+ else
+ {
+ m_player.Stop(true);
+ }
+
+ PseudoFile file;
+ file.data = &m_sseq.sdatData;
+
+ m_sseq.sdat.reset(new SDAT(file, m_sseq.sseq));
+
+ auto* sseqToPlay = m_sseq.sdat->sseq.get();
+
+ m_player.sseqVol = Cnv_Scale(sseqToPlay->info.vol);
+ m_player.sampleRate = m_cfgDefaultSampleRate;
+ m_player.interpolation = INTERPOLATION_SINC;
+ m_player.Setup(sseqToPlay);
+ m_player.Timer();
+
+ m_startSilence = 0;
+ m_silence = 0;
+
+ m_eof = false;
+ m_dataWritten = 0;
+ m_remainder = 0;
+ m_posDelta = 0;
+ m_pos = 0;
+
+ calcfade();
+
+ m_sampleBuffer.resize(4096 * 2 * sizeof(int16_t), 0);
+
+ unsigned skip_max = m_cfgEndSilenceSeconds * m_player.sampleRate;
+
+ if (m_cfgSuppressOpeningSilence) // ohcrap
+ {
+ for (;;)
{
- case 1:
- value *= 100;
+ unsigned int skip_howmany = skip_max - m_silence;
+ unsigned int unskippable = 0;
+ if (skip_howmany > 1024)
+ skip_howmany = 1024;
+ m_player.GenerateSamples(m_sampleBuffer, 0, skip_howmany);
+ int16_t* foo = reinterpret_cast(m_sampleBuffer.data());
+ unsigned int i;
+ for (i = 0; i < skip_howmany; ++i)
+ {
+ if (foo[0] || foo[1])
+ break;
+ foo += 2;
+ }
+ m_silence += i;
+ if (i < skip_howmany)
+ {
+ m_remainder = skip_howmany - i + unskippable;
+ memmove(m_sampleBuffer.data(), foo, m_remainder * sizeof(int16_t) * 2);
break;
- case 2:
- value *= 10;
+ }
+ if (m_silence >= skip_max)
+ {
+ m_eof = true;
break;
+ }
}
- strs--;
- *strs = 0;
- strs--;
- }
- while (strs > bar && (*strs >= '0' && *strs <= '9'))
- {
- strs--;
- }
- // seconds
- if (*strs < '0' || *strs > '9') strs++;
- value += atoi(strs) * 1000;
- if (strs > bar)
- {
- strs--;
- *strs = 0;
- strs--;
- while (strs > bar && (*strs >= '0' && *strs <= '9'))
- {
- strs--;
- }
- if (*strs < '0' || *strs > '9') strs++;
- value += atoi(strs) * 60000;
- if (strs > bar)
+
+ m_startSilence += m_silence;
+ m_silence = 0;
+ }
+
+ if (m_cfgSuppressEndSilence)
+ m_silenceTestBuffer.resize(skip_max * 2);
+
+ return true;
+}
+
+int CNCSFCodec::ReadPCM(uint8_t* buffer, int size, int& actualsize)
+{
+ if (m_eof && !m_silenceTestBuffer.data_available())
+ return -1;
+
+ if (m_tagSongMs && (m_posDelta + mul_div(m_dataWritten, 1000, m_player.sampleRate)) >= m_tagSongMs + m_tagFadeMs)
+ return 1;
+
+ if (size > m_sampleBuffer.size())
+ m_sampleBuffer.resize(size * 2);
+
+ unsigned int written = 0;
+
+ int usedSize = size / 2 / sizeof(int16_t);
+
+ int samples = (m_songLength + m_fadeLength) - m_dataWritten;
+ if (samples > usedSize)
+ samples = usedSize;
+
+ if (m_cfgSuppressEndSilence)
+ {
+ if (!m_eof)
{
- strs--;
- *strs = 0;
- strs--;
- while (strs > bar && (*strs >= '0' && *strs <= '9'))
+ unsigned int free_space = m_silenceTestBuffer.free_space() / 2;
+ while (free_space)
{
- strs--;
+ unsigned int samples_to_render;
+ if (m_remainder)
+ {
+ samples_to_render = m_remainder;
+ m_remainder = 0;
+ }
+ else
+ {
+ samples_to_render = free_space;
+ if (samples_to_render > usedSize)
+ samples_to_render = usedSize;
+ m_player.GenerateSamples(m_sampleBuffer, 0, samples_to_render);
+ }
+ m_silenceTestBuffer.write(reinterpret_cast(m_sampleBuffer.data()), samples_to_render * 2);
+ free_space -= samples_to_render;
+ if (m_remainder)
+ {
+ memmove(m_sampleBuffer.data(), reinterpret_cast(m_sampleBuffer.data()) + samples_to_render * 2, m_remainder * 4);
+ }
}
- value += atoi(strs) * 3600000;
+ }
+
+ if (m_silenceTestBuffer.test_silence())
+ {
+ m_eof = true;
+ return -1;
+ }
+
+ written = m_silenceTestBuffer.data_available() / 2;
+ if (written > samples)
+ written = samples;
+ m_silenceTestBuffer.read(reinterpret_cast(m_sampleBuffer.data()), written * 2);
+ }
+ else
+ {
+ if (m_remainder)
+ {
+ written = m_remainder;
+ m_remainder = 0;
+ }
+ else
+ {
+ written = samples;
+ m_player.GenerateSamples(m_sampleBuffer, 0, written);
}
}
- return value;
-}
-static int psf_info_meta(void * context, const char * name, const char * value)
-{
- NCSFContext* ncsf = (NCSFContext*)context;
+ m_pos += double(written) / double(m_player.sampleRate);
- if (!strcasecmp(name, "game"))
- ncsf->album = value;
- else if (!strcasecmp(name, "year"))
- ncsf->year = atoi(value);
- else if (!strcasecmp(name, "length"))
+ int d_start, d_end;
+ d_start = m_dataWritten;
+ m_dataWritten += written;
+ d_end = m_dataWritten;
+
+ if (m_tagSongMs && d_end > m_songLength)
{
- int temp = parse_time_crap(value);
- if (temp != BORK_TIME)
- ncsf->length = temp;
+ int16_t* foo = reinterpret_cast(m_sampleBuffer.data());
+ for (int n = d_start; n < d_end; ++n)
+ {
+ if (n > m_songLength)
+ {
+ if (n > m_songLength + m_fadeLength)
+ {
+ *(uint32_t*)foo = 0;
+ }
+ else
+ {
+ int bleh = m_songLength + m_fadeLength - n;
+ foo[0] = mul_div(foo[0], bleh, m_fadeLength);
+ foo[1] = mul_div(foo[1], bleh, m_fadeLength);
+ }
+ }
+ foo += 2;
+ }
}
+ actualsize = written * 2 * sizeof(int16_t);
+ memcpy(buffer, m_sampleBuffer.data(), actualsize);
+
return 0;
}
-inline unsigned get_le32( void const* p )
+int64_t CNCSFCodec::Seek(int64_t time)
{
- return (unsigned) ((unsigned char const*) p) [3] << 24 |
- (unsigned) ((unsigned char const*) p) [2] << 16 |
- (unsigned) ((unsigned char const*) p) [1] << 8 |
- (unsigned) ((unsigned char const*) p) [0];
-}
+ double p_seconds = double(time) / 1000.0;
+ m_eof = false;
-int ncsf_loader(void * context, const uint8_t * exe, size_t exe_size,
- const uint8_t * reserved, size_t reserved_size)
-{
- struct ncsf_loader_state * state = ( struct ncsf_loader_state * ) context;
+ double buffered_time = (double)(m_silenceTestBuffer.data_available() / 2) / m_cfgDefaultSampleRate;
+
+ m_pos += buffered_time;
+
+ m_silenceTestBuffer.reset();
- if ( reserved_size >= 4 )
+ if (p_seconds < m_pos)
{
- state->sseq = get_le32( reserved );
+ Load();
}
+ unsigned int howmany = (int)(time_to_samples(p_seconds - m_pos, m_cfgDefaultSampleRate));
- if ( exe_size >= 12 )
+ // more abortable, and emu doesn't like doing huge numbers of samples per call anyway
+ while (howmany)
{
- uint32_t sdat_size = get_le32( exe + 8 );
- if ( sdat_size > exe_size ) return -1;
-
- if ( state->sdatData.empty() )
- state->sdatData.resize( sdat_size, 0 );
- else if ( state->sdatData.size() < sdat_size )
- state->sdatData.resize( sdat_size );
- memcpy( &state->sdatData[0], exe, sdat_size );
+ unsigned int samples = 1024;
+ m_player.GenerateSamples(m_sampleBuffer, 0, samples);
+ if (samples > howmany)
+ {
+ memmove(m_sampleBuffer.data(), reinterpret_cast(m_sampleBuffer.data()) + howmany * 2, (samples - howmany) * 4);
+ m_remainder = samples - howmany;
+ samples = howmany;
+ }
+ howmany -= samples;
}
- return 0;
+ m_dataWritten = 0;
+ m_posDelta = (int)(p_seconds * 1000.);
+ m_pos = p_seconds;
+
+ calcfade();
+
+ return time;
}
-static bool Load(NCSFContext* r)
+bool CNCSFCodec::ReadTag(const std::string& file, std::string& title,
+ std::string& artist, int& length)
{
- if (psf_load(r->file.c_str(), &psf_file_system, 0x25,
- 0, 0, psf_info_meta, r, 0) <= 0)
+ NCSFContext result;
+ int ret = psf_load(file.c_str(), &psf_file_system, 0x25,
+ nullptr, nullptr,
+ NCFSInfoMeta, &result, 0,
+ NCFSPrintMessage, this);
+ if (ret <= 0)
{
- delete r;
+ kodi::Log(ADDON_LOG_ERROR, "%s: Not an NCSF file (%s)", __func__, file.c_str());
return false;
}
- if (psf_load(r->file.c_str(), &psf_file_system, 0x25,
- ncsf_loader, &r->sseq, 0, 0, 0) < 0)
+ // TODO: Change ReadTag in Kodi to give also other parts.
+ if (result.title.empty())
{
- delete r;
- return false;
+ std::string fileName = kodi::vfs::GetFileName(file);
+ size_t lastindex = fileName.find_last_of(".");
+ title = fileName.substr(0, lastindex);
+ }
+ else
+ {
+ if (!result.track.empty() && !result.disc.empty())
+ title = result.disc + "." + result.track + " - " + result.title;
+ else if (!result.track.empty())
+ title = result.track + " - " + result.title;
+ else
+ title = result.title;
}
- r->player.Stop(true);
- PseudoFile file;
- file.data = &r->sseq.sdatData;
- r->sseq.sdat.reset(new SDAT(file, r->sseq.sseq));
- auto* sseqToPlay = r->sseq.sdat->sseq.get();
- r->player.sseqVol = Cnv_Scale(sseqToPlay->info.vol);
- r->player.sampleRate = 48000;
- r->player.interpolation = INTERPOLATION_SINC;
- r->player.Setup(sseqToPlay);
- r->player.Timer();
-
- r->pos = 0;
+ artist = result.game;
+ length = result.tagSongMs/1000;
return true;
}
-class ATTRIBUTE_HIDDEN CNCSFCodec : public kodi::addon::CInstanceAudioDecoder
+void CNCSFCodec::NCFSPrintMessage(void* context, const char* message)
{
-public:
- CNCSFCodec(KODI_HANDLE instance) :
- CInstanceAudioDecoder(instance) {}
+ kodi::Log(ADDON_LOG_DEBUG, "NCFS codec message: '%s'", message);
+}
- virtual ~CNCSFCodec()
- {
- }
+int CNCSFCodec::NCSFLoader(void* context, const uint8_t* exe, size_t exe_size,
+ const uint8_t* reserved, size_t reserved_size)
+{
+ NCSFLoaderState* state = static_cast(context);
- virtual bool Init(const std::string& filename, unsigned int filecache,
- int& channels, int& samplerate,
- int& bitspersample, int64_t& totaltime,
- int& bitrate, AEDataFormat& format,
- std::vector& channellist) override
+ if (reserved_size >= 4)
{
- ctx.sample_buffer.Create(16384);
- ctx.file = filename;
- if (!Load(&ctx))
- return false;
-
- totaltime = ctx.length;
- format = AE_FMT_S16NE;
- channellist = { AE_CH_FL, AE_CH_FR };
- channels = 2;
- bitspersample = 16;
- bitrate = 0.0;
- samplerate = 48000;
-
- return true;
- }
-
- virtual int ReadPCM(uint8_t* buffer, int size, int& actualsize) override
- {
- if (ctx.pos >= ctx.length*48000*4/1000)
- return 1;
-
- if (ctx.sample_buffer.getMaxReadSize() == 0) {
- std::vector temp(2048*2*2);
- unsigned written=2048;
- ctx.player.GenerateSamples(temp, 0, written);
- ctx.sample_buffer.WriteData((const char*)&temp[0], written*4);
- }
-
- int tocopy = std::min(size, (int)ctx.sample_buffer.getMaxReadSize());
- ctx.sample_buffer.ReadData((char*)buffer, tocopy);
- ctx.pos += tocopy;
- actualsize = tocopy;
- return 0;
+ state->sseq = get_le32(reserved);
}
- virtual int64_t Seek(int64_t time) override
+ if (exe_size >= 12)
{
- int64_t pos = time*48000*4/1000;
- if (pos < ctx.pos)
- Load(&ctx);
+ uint32_t sdat_size = get_le32(exe + 8);
+ if (sdat_size > exe_size)
+ return -1;
- std::vector temp(2048*2*2);
- while (ctx.pos < pos)
- {
- unsigned written=2048;
- ctx.player.GenerateSamples(temp, 0, written);
- ctx.pos += written*4;
- }
+ if (state->sdatData.empty())
+ state->sdatData.resize(sdat_size, 0);
+ else if (state->sdatData.size() < sdat_size)
+ state->sdatData.resize(sdat_size);
+ memcpy(&state->sdatData[0], exe, sdat_size);
+ }
- int64_t overshoot = ctx.pos-pos;
+ return 0;
+}
- ctx.sample_buffer.Clear();
- ctx.sample_buffer.WriteData((const char*)&temp[0], overshoot);
- ctx.pos = pos;
+int CNCSFCodec::NCFSInfoMeta(void* context, const char* name, const char* value)
+{
+ NCSFContext* ncsf = static_cast(context);
- return time;
+ if (!strcasecmp(name, "artist"))
+ ncsf->artist = value;
+ else if (!strcasecmp(name, "title"))
+ ncsf->title = value;
+ else if (!strcasecmp(name, "game"))
+ ncsf->game = value;
+ else if (!strcasecmp(name, "copyright"))
+ ncsf->copyright = value;
+ else if (!strcasecmp(name, "comment"))
+ ncsf->comment = value;
+ else if (!strcasecmp(name, "year"))
+ ncsf->year = atoi(value);
+ else if (!strcasecmp(name, "disc"))
+ ncsf->disc = value;
+ else if (!strcasecmp(name, "track"))
+ ncsf->track = value;
+ else if (!strcasecmp(name, "length"))
+ {
+ int temp = parse_time_crap(value);
+ if (temp != BORK_TIME)
+ ncsf->tagSongMs = temp;
}
-
- virtual bool ReadTag(const std::string& file, std::string& title,
- std::string& artist, int& length) override
+ else if (!strcasecmp(name, "fade"))
{
- NCSFContext result;
- if (psf_load(file.c_str(), &psf_file_system, 0x25, 0, 0, psf_info_meta, &result, 0) <= 0)
- return false;
-
- const char* rslash = strrchr(file.c_str(),'/');
- if (!rslash)
- rslash = strrchr(file.c_str(),'\\');
- title = rslash+1;
- artist = result.album;
- length = result.length/1000;
- return true;
+ int temp = parse_time_crap(value);
+ if (temp != BORK_TIME)
+ ncsf->tagFadeMs = temp;
}
-private:
- NCSFContext ctx;
-};
+ return 0;
+}
+//------------------------------------------------------------------------------
class ATTRIBUTE_HIDDEN CMyAddon : public kodi::addon::CAddonBase
{
public:
- CMyAddon() { }
- virtual ADDON_STATUS CreateInstance(int instanceType, std::string instanceID, KODI_HANDLE instance, KODI_HANDLE& addonInstance) override
+ CMyAddon() = default;
+ ADDON_STATUS CreateInstance(int instanceType, std::string instanceID, KODI_HANDLE instance, KODI_HANDLE& addonInstance) override
{
addonInstance = new CNCSFCodec(instance);
return ADDON_STATUS_OK;
}
- virtual ~CMyAddon()
- {
- }
+ ~CMyAddon() override = default;
};
-
-ADDONCREATOR(CMyAddon);
+ADDONCREATOR(CMyAddon)
diff -Nru kodi-audiodecoder-ncsf-2.1.0/src/NCSFCodec.h kodi-audiodecoder-ncsf-2.2.0/src/NCSFCodec.h
--- kodi-audiodecoder-ncsf-2.1.0/src/NCSFCodec.h 1970-01-01 00:00:00.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/src/NCSFCodec.h 2013-05-31 22:59:22.000000000 +0000
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 Team Kodi
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#pragma once
+
+#include "CircularBuffer.h"
+
+#include "SSEQPlayer/Player.h"
+#include "SSEQPlayer/SDAT.h"
+
+#include
+#include
+#include
+#include
+
+struct NCSFLoaderState
+{
+ uint32_t sseq = 0;
+ std::vector sdatData;
+ std::unique_ptr sdat;
+};
+
+struct NCSFContext
+{
+ int year = 0;
+ int tagSongMs = 0;
+ int tagFadeMs = 0;
+
+ std::string artist;
+ std::string title;
+ std::string game;
+ std::string copyright;
+ std::string comment;
+ std::string disc;
+ std::string track;
+};
+
+class ATTRIBUTE_HIDDEN CNCSFCodec : public kodi::addon::CInstanceAudioDecoder
+{
+public:
+ CNCSFCodec(KODI_HANDLE instance) :
+ CInstanceAudioDecoder(instance) {}
+ ~CNCSFCodec() override = default;
+
+ bool Init(const std::string& filename, unsigned int filecache,
+ int& channels, int& samplerate,
+ int& bitspersample, int64_t& totaltime,
+ int& bitrate, AEDataFormat& format,
+ std::vector& channellist) override;
+ int ReadPCM(uint8_t* buffer, int size, int& actualsize) override;
+ int64_t Seek(int64_t time) override;
+ bool ReadTag(const std::string& file, std::string& title,
+ std::string& artist, int& length) override;
+
+private:
+ static void NCFSPrintMessage(void* context, const char* message);
+ static int NCSFLoader(void * context, const uint8_t * exe, size_t exe_size,
+ const uint8_t * reserved, size_t reserved_size);
+ static int NCFSInfoMeta(void* context, const char* name, const char* value);
+
+ bool Load();
+
+ inline uint64_t time_to_samples(double p_time, uint32_t p_sample_rate)
+ {
+ return (uint64_t)floor((double)p_sample_rate * p_time + 0.5);
+ }
+
+ inline void calcfade()
+ {
+ m_songLength = mul_div(m_tagSongMs-m_posDelta, m_cfgDefaultSampleRate, 1000);
+ m_fadeLength = mul_div(m_tagFadeMs, m_cfgDefaultSampleRate, 1000);
+ }
+
+ inline int mul_div(int number, int numerator, int denominator)
+ {
+ long long ret = number;
+ ret *= numerator;
+ ret /= denominator;
+ return (int) ret;
+ }
+
+ int m_cfgDefaultSampleRate = 48000;
+ bool m_cfgSuppressOpeningSilence = true;
+ bool m_cfgSuppressEndSilence = true;
+ int m_cfgEndSilenceSeconds = 5;
+
+ std::string m_file;
+
+ bool m_eof;
+ int m_dataWritten;
+ int m_remainder;
+ int m_posDelta;
+ int m_startSilence;
+ int m_silence;
+
+ double m_pos;
+
+ int m_songLength;
+ int m_fadeLength;
+ int m_tagSongMs;
+ int m_tagFadeMs;
+
+ Player m_player;
+ circular_buffer m_silenceTestBuffer = 0;
+ std::vector m_sampleBuffer;
+ NCSFLoaderState m_sseq;
+};
diff -Nru kodi-audiodecoder-ncsf-2.1.0/src/RingBuffer.cpp kodi-audiodecoder-ncsf-2.2.0/src/RingBuffer.cpp
--- kodi-audiodecoder-ncsf-2.1.0/src/RingBuffer.cpp 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/src/RingBuffer.cpp 1970-01-01 00:00:00.000000000 +0000
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Team XBMC
- * http://xbmc.org
- *
- * This Program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This Program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, see
- * .
- *
- */
-
-#include "RingBuffer.h"
-
-#include
-#include
-#include
-
-/* Constructor */
-CRingBuffer::CRingBuffer()
-{
- m_buffer = NULL;
- m_size = 0;
- m_readPtr = 0;
- m_writePtr = 0;
- m_fillCount = 0;
-}
-
-/* Destructor */
-CRingBuffer::~CRingBuffer()
-{
- Destroy();
-}
-
-/* Create a ring buffer with the specified 'size' */
-bool CRingBuffer::Create(unsigned int size)
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- m_buffer = (char*)malloc(size);
- if (m_buffer != NULL)
- {
- m_size = size;
- return true;
- }
- return false;
-}
-
-/* Free the ring buffer and set all values to NULL or 0 */
-void CRingBuffer::Destroy()
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- if (m_buffer != NULL)
- {
- free(m_buffer);
- m_buffer = NULL;
- }
- m_size = 0;
- m_readPtr = 0;
- m_writePtr = 0;
- m_fillCount = 0;
-}
-
-/* Clear the ring buffer */
-void CRingBuffer::Clear()
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- m_readPtr = 0;
- m_writePtr = 0;
- m_fillCount = 0;
-}
-
-/* Read in data from the ring buffer to the supplied buffer 'buf'. The amount
- * read in is specified by 'size'.
- */
-bool CRingBuffer::ReadData(char *buf, unsigned int size)
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- if (size > m_fillCount)
- {
- return false;
- }
- if (size + m_readPtr > m_size)
- {
- unsigned int chunk = m_size - m_readPtr;
- memcpy(buf, m_buffer + m_readPtr, chunk);
- memcpy(buf + chunk, m_buffer, size - chunk);
- m_readPtr = size - chunk;
- }
- else
- {
- memcpy(buf, m_buffer + m_readPtr, size);
- m_readPtr += size;
- }
- if (m_readPtr == m_size)
- m_readPtr = 0;
- m_fillCount -= size;
- return true;
-}
-
-/* Read in data from the ring buffer to another ring buffer object specified by
- * 'rBuf'.
- */
-bool CRingBuffer::ReadData(CRingBuffer &rBuf, unsigned int size)
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- if (rBuf.getBuffer() == NULL)
- rBuf.Create(size);
-
- bool bOk = size <= rBuf.getMaxWriteSize() && size <= getMaxReadSize();
- if (bOk)
- {
- unsigned int chunksize = std::min(size, m_size - m_readPtr);
- bOk = rBuf.WriteData(&getBuffer()[m_readPtr], chunksize);
- if (bOk && chunksize < size)
- bOk = rBuf.WriteData(&getBuffer()[0], size - chunksize);
- if (bOk)
- SkipBytes(size);
- }
-
- return bOk;
-}
-
-/* Write data to ring buffer from buffer specified in 'buf'. Amount read in is
- * specified by 'size'.
- */
-bool CRingBuffer::WriteData(const char *buf, unsigned int size)
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- if (size > m_size - m_fillCount)
- {
- return false;
- }
- if (size + m_writePtr > m_size)
- {
- unsigned int chunk = m_size - m_writePtr;
- memcpy(m_buffer + m_writePtr, buf, chunk);
- memcpy(m_buffer, buf + chunk, size - chunk);
- m_writePtr = size - chunk;
- }
- else
- {
- memcpy(m_buffer + m_writePtr, buf, size);
- m_writePtr += size;
- }
- if (m_writePtr == m_size)
- m_writePtr = 0;
- m_fillCount += size;
- return true;
-}
-
-/* Write data to ring buffer from another ring buffer object specified by
- * 'rBuf'.
- */
-bool CRingBuffer::WriteData(CRingBuffer &rBuf, unsigned int size)
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- if (m_buffer == NULL)
- Create(size);
-
- bool bOk = size <= rBuf.getMaxReadSize() && size <= getMaxWriteSize();
- if (bOk)
- {
- unsigned int readpos = rBuf.getReadPtr();
- unsigned int chunksize = std::min(size, rBuf.getSize() - readpos);
- bOk = WriteData(&rBuf.getBuffer()[readpos], chunksize);
- if (bOk && chunksize < size)
- bOk = WriteData(&rBuf.getBuffer()[0], size - chunksize);
- }
-
- return bOk;
-}
-
-/* Skip bytes in buffer to be read */
-bool CRingBuffer::SkipBytes(int skipSize)
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- if (skipSize < 0)
- {
- return false; // skipping backwards is not supported
- }
-
- unsigned int size = skipSize;
- if (size > m_fillCount)
- {
- return false;
- }
- if (size + m_readPtr > m_size)
- {
- unsigned int chunk = m_size - m_readPtr;
- m_readPtr = size - chunk;
- }
- else
- {
- m_readPtr += size;
- }
- if (m_readPtr == m_size)
- m_readPtr = 0;
- m_fillCount -= size;
- return true;
-}
-
-/* Append all content from ring buffer 'rBuf' to this ring buffer */
-bool CRingBuffer::Append(CRingBuffer &rBuf)
-{
- return WriteData(rBuf, rBuf.getMaxReadSize());
-}
-
-/* Copy all content from ring buffer 'rBuf' to this ring buffer overwriting any existing data */
-bool CRingBuffer::Copy(CRingBuffer &rBuf)
-{
- Clear();
- return Append(rBuf);
-}
-
-/* Our various 'get' methods */
-char *CRingBuffer::getBuffer()
-{
- return m_buffer;
-}
-
-unsigned int CRingBuffer::getSize()
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- return m_size;
-}
-
-unsigned int CRingBuffer::getReadPtr() const
-{
- return m_readPtr;
-}
-
-unsigned int CRingBuffer::getWritePtr()
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- return m_writePtr;
-}
-
-unsigned int CRingBuffer::getMaxReadSize()
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- return m_fillCount;
-}
-
-unsigned int CRingBuffer::getMaxWriteSize()
-{
- P8PLATFORM::CLockObject lock(m_critSection, true);
- return m_size - m_fillCount;
-}
diff -Nru kodi-audiodecoder-ncsf-2.1.0/src/RingBuffer.h kodi-audiodecoder-ncsf-2.2.0/src/RingBuffer.h
--- kodi-audiodecoder-ncsf-2.1.0/src/RingBuffer.h 2013-05-31 22:59:22.000000000 +0000
+++ kodi-audiodecoder-ncsf-2.2.0/src/RingBuffer.h 1970-01-01 00:00:00.000000000 +0000
@@ -1,51 +0,0 @@
-#pragma once
-/*
- * Copyright (C) 2010-2013 Team XBMC
- * http://xbmc.org
- *
- * This Program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This Program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with XBMC; see the file COPYING. If not, see
- * .
- *
- */
-
-#include "p8-platform/threads/mutex.h"
-
-class CRingBuffer
-{
- P8PLATFORM::CMutex m_critSection;
- char *m_buffer;
- unsigned int m_size;
- unsigned int m_readPtr;
- unsigned int m_writePtr;
- unsigned int m_fillCount;
-public:
- CRingBuffer();
- ~CRingBuffer();
- bool Create(unsigned int size);
- void Destroy();
- void Clear();
- bool ReadData(char *buf, unsigned int size);
- bool ReadData(CRingBuffer &rBuf, unsigned int size);
- bool WriteData(const char *buf, unsigned int size);
- bool WriteData(CRingBuffer &rBuf, unsigned int size);
- bool SkipBytes(int skipSize);
- bool Append(CRingBuffer &rBuf);
- bool Copy(CRingBuffer &rBuf);
- char *getBuffer();
- unsigned int getSize();
- unsigned int getReadPtr() const;
- unsigned int getWritePtr();
- unsigned int getMaxReadSize();
- unsigned int getMaxWriteSize();
-};