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(); -};