aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/file
diff options
context:
space:
mode:
Diffstat (limited to 'Src/replicant/file')
-rw-r--r--Src/replicant/file/FileDecoder.h18
-rw-r--r--Src/replicant/file/FileMetadata.h112
-rw-r--r--Src/replicant/file/FilePlayback.h54
-rw-r--r--Src/replicant/file/FileRawReader.h18
-rw-r--r--Src/replicant/file/api.h12
-rw-r--r--Src/replicant/file/ifc_filemetadata_editor.h43
-rw-r--r--Src/replicant/file/ifc_fileplayback.h77
-rw-r--r--Src/replicant/file/main.h2
-rw-r--r--Src/replicant/file/precomp.h32
-rw-r--r--Src/replicant/file/svc_filedecode.h32
-rw-r--r--Src/replicant/file/svc_filemetadata.h30
-rw-r--r--Src/replicant/file/svc_fileplayback.h30
-rw-r--r--Src/replicant/file/svc_filerawreader.h30
13 files changed, 490 insertions, 0 deletions
diff --git a/Src/replicant/file/FileDecoder.h b/Src/replicant/file/FileDecoder.h
new file mode 100644
index 00000000..2ac59fd6
--- /dev/null
+++ b/Src/replicant/file/FileDecoder.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "decode/svc_decode.h"
+#include "nswasabi/ServiceName.h"
+
+// {F07160ED-2820-4DA5-9CE9-857A4CD9DFA0}
+static const GUID file_decoder_guid =
+{ 0xf07160ed, 0x2820, 0x4da5, { 0x9c, 0xe9, 0x85, 0x7a, 0x4c, 0xd9, 0xdf, 0xa0 } };
+
+class FileDecoderService : public svc_decode
+{
+public:
+ WASABI_SERVICE_NAME("File Decoder");
+ WASABI_SERVICE_GUID(file_decoder_guid);
+
+private:
+ int WASABICALL DecodeService_CreateAudioDecoder_Callback(unsigned int pass, ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags);
+ int WASABICALL DecodeService_CreateAudioDecoder_Pull(unsigned int pass, ifc_audio_decoder_pull **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags);
+}; \ No newline at end of file
diff --git a/Src/replicant/file/FileMetadata.h b/Src/replicant/file/FileMetadata.h
new file mode 100644
index 00000000..1b231c4d
--- /dev/null
+++ b/Src/replicant/file/FileMetadata.h
@@ -0,0 +1,112 @@
+#pragma once
+#include "metadata/svc_metadata.h"
+#include "metadata/ifc_metadata.h"
+#include "nswasabi/ServiceName.h"
+#include "nx/nxuri.h"
+#include "nx/nxfile.h"
+#include "nsid3v2/nsid3v2.h"
+#include "nsid3v1/nsid3v1.h"
+#include "nsapev2/nsapev2.h"
+#include "nswasabi/ID3v2Metadata.h"
+#include "nswasabi/APEv2Metadata.h"
+#include "nswasabi/ID3v1Metadata.h"
+#include "ifc_filemetadata_editor.h"
+
+// {6FCF1A5A-79D0-4FB8-9911-222638CA32A1}
+static const GUID file_metadata_guid =
+{ 0x6fcf1a5a, 0x79d0, 0x4fb8, { 0x99, 0x11, 0x22, 0x26, 0x38, 0xca, 0x32, 0xa1 } };
+
+
+class FileMetadataService : public svc_metadata
+{
+public:
+ WASABI_SERVICE_NAME("File Metadata");
+ WASABI_SERVICE_GUID(file_metadata_guid);
+
+private:
+ int WASABICALL MetadataService_EnumerateExtensions(unsigned int index, nx_string_t *extension);
+ int WASABICALL MetadataService_CreateMetadata(unsigned int pass, nx_uri_t filename, ifc_metadata **metadata);
+ int WASABICALL MetadataService_CreateMetadataEditor(unsigned int pass, nx_uri_t filename, ifc_metadata_editor **metadata);
+};
+
+class FileMetadata
+{
+public:
+ ns_error_t SetFileInformation(nx_uri_t filename, nx_file_stat_t file_stat);
+ ns_error_t FindMetadata(nx_file_t file);
+ bool HasMetadata() const; // returns whether or not there was any metadata found
+
+protected:
+ FileMetadata();
+ ~FileMetadata();
+
+ ns_error_t OwnID3v2(nsid3v2_tag_t id3v2, uint64_t position, uint64_t length);
+ ns_error_t OwnID3v1(nsid3v1_tag_t id3v1, uint64_t position, uint64_t length);
+ ns_error_t OwnAPEv2(nsapev2_tag_t apev2, uint64_t position, uint64_t length);
+ ns_error_t OwnLyrics3(void *lyrics, uint64_t position, uint64_t length);
+
+ nx_uri_t filename;
+ nx_file_stat_s file_stat;
+
+ template <class tag_t>
+ class MetaTag
+ {
+ public:
+ MetaTag() : tag(0), position(0), length(0) {}
+ tag_t tag;
+ uint64_t position;
+ uint64_t length;
+ };
+
+ /* ID3v2 */
+ MetaTag<nsid3v2_tag_t> id3v2;
+ ID3v2Metadata id3v2_metadata;
+
+ /* ID3v1 */
+ MetaTag<nsid3v1_tag_t> id3v1;
+ ID3v1Metadata id3v1_metadata;
+
+ /* APEv2 */
+ MetaTag<nsapev2_tag_t> apev2;
+ APEv2Metadata apev2_metadata;
+
+ MetaTag<void *> lyrics3;
+
+ uint64_t start_position;
+ uint64_t content_length;
+ uint64_t end_position;
+};
+
+class FileMetadataRead : public FileMetadata, public ifc_metadata
+{
+private:
+ int WASABICALL Metadata_GetField(int field, unsigned int index, nx_string_t *value);
+ int WASABICALL Metadata_GetInteger(int field, unsigned int index, int64_t *value);
+ int WASABICALL Metadata_GetReal(int field, unsigned int index, double *value);
+
+ int WASABICALL Metadata_GetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags);
+ int WASABICALL Metadata_GetBinary(int field, unsigned int index, nx_data_t *data);
+ int WASABICALL Metadata_GetMetadata(int field, unsigned int index, ifc_metadata **metadata);
+
+ int WASABICALL Metadata_Serialize(nx_data_t *data);
+};
+
+class FileMetadataWrite : public FileMetadata, public ifc_metadata_editor
+{
+public:
+ FileMetadataWrite();
+ ~FileMetadataWrite();
+ int Initialize(ifc_filemetadata_editor *editor);
+private:
+ /* ifc_metadata_editor implementation */
+ int WASABICALL MetadataEditor_Save();
+ int WASABICALL MetadataEditor_SaveAs(nx_uri_t destination);
+
+ int WASABICALL MetadataEditor_SetField(int field, unsigned int index, nx_string_t value);
+ int WASABICALL MetadataEditor_SetInteger(int field, unsigned int index, int64_t value);
+ int WASABICALL MetadataEditor_SetReal(int field, unsigned int index, double value);
+ int WASABICALL MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags);
+
+ int MakeID3v2();
+ ifc_filemetadata_editor *editor;
+};
diff --git a/Src/replicant/file/FilePlayback.h b/Src/replicant/file/FilePlayback.h
new file mode 100644
index 00000000..b6b17bec
--- /dev/null
+++ b/Src/replicant/file/FilePlayback.h
@@ -0,0 +1,54 @@
+#pragma once
+#include "player/svc_playback.h"
+#include "nx/nxstring.h"
+#include "nx/nxfile.h"
+#include "nswasabi/ServiceName.h"
+#include "nswasabi/PlaybackBase.h"
+#include "ifc_fileplayback.h"
+#include "replaygain/ifc_replaygain_settings.h"
+#include "FileMetadata.h"
+
+// {60DB6A52-1A82-4C0D-A947-203549260758}
+static const GUID file_playback_guid =
+{ 0x60db6a52, 0x1a82, 0x4c0d, { 0xa9, 0x47, 0x20, 0x35, 0x49, 0x26, 0x7, 0x58 } };
+
+class FilePlaybackService : public svc_playback
+{
+public:
+ WASABI_SERVICE_NAME("File Playback");
+ static GUID GetServiceGUID() { return file_playback_guid; }
+ FilePlaybackService();
+ int WASABICALL PlaybackService_CreatePlayback(unsigned int pass, nx_uri_t filename, ifc_player *player, ifc_playback **out_playback_object);
+};
+
+class FilePlayback : public PlaybackBase, public ifc_fileplayback_parent
+{
+public:
+ FilePlayback();
+ ~FilePlayback();
+ ns_error_t Initialize(nx_uri_t filename, ifc_player *player);
+private:
+ ns_error_t WASABICALL FilePlaybackParent_OpenOutput(const ifc_audioout::Parameters *parameters);
+ ns_error_t WASABICALL FilePlaybackParent_Output(const void *audio_data, size_t audio_data_length, size_t *frames_consumed, double begin_position_seconds);
+ ns_error_t WASABICALL FilePlaybackParent_OutputNonInterleaved(const void *audio_data, size_t audio_data_length, size_t *frames_consumed, double begin_position_seconds);
+ ns_error_t WASABICALL FilePlaybackParent_OnMetadata(ifc_metadata *new_metadata);
+
+ ifc_fileplayback *implementation;
+ nx_file_t file;
+ ifc_audioout *out;
+ bool paused;
+ double last_position;
+ ns_error_t exact_length, exact_bitrate;
+ ifc_audioout::Parameters parameters;
+ const uint8_t **output_pointers;
+ ifc_metadata *implementation_metadata;
+ FileMetadataRead *metadata;
+
+ ns_error_t Internal_Interrupt();
+ ns_error_t OutputWait();
+ ns_error_t WaitForClose();
+
+ /* Thread function */
+ static nx_thread_return_t NXTHREADCALL FilePlayerThreadFunction(nx_thread_parameter_t param);
+ nx_thread_return_t NXTHREADCALL DecodeLoop();
+}; \ No newline at end of file
diff --git a/Src/replicant/file/FileRawReader.h b/Src/replicant/file/FileRawReader.h
new file mode 100644
index 00000000..b90fa359
--- /dev/null
+++ b/Src/replicant/file/FileRawReader.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "decode/svc_raw_media_reader.h"
+#include "decode/ifc_raw_media_reader.h"
+#include "nswasabi/ServiceName.h"
+#include "svc_filerawreader.h"
+
+// {3877B6CF-937B-4E83-9A88-02E16B3A9654}
+static const GUID file_raw_reader_guid =
+{ 0x3877b6cf, 0x937b, 0x4e83, { 0x9a, 0x88, 0x2, 0xe1, 0x6b, 0x3a, 0x96, 0x54 } };
+
+class FileRawReaderService : public svc_raw_media_reader
+{
+public:
+ WASABI_SERVICE_NAME("File Raw Reader");
+ WASABI_SERVICE_GUID(file_raw_reader_guid);
+
+ int WASABICALL RawMediaReaderService_CreateRawMediaReader(ifc_raw_media_reader **reader, nx_uri_t filename, unsigned int pass);
+};
diff --git a/Src/replicant/file/api.h b/Src/replicant/file/api.h
new file mode 100644
index 00000000..2cda1293
--- /dev/null
+++ b/Src/replicant/file/api.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "service/api_service.h"
+#include "application/api_application.h"
+#include "syscb/api_syscb.h"
+#include "metadata/api_metadata.h"
+#include "filelock/api_filelock.h"
+
+DECLARE_EXTERNAL_SERVICE(api_service, WASABI2_API_SVC);
+DECLARE_EXTERNAL_SERVICE(api_application, WASABI2_API_APP);
+DECLARE_EXTERNAL_SERVICE(api_syscb, WASABI2_API_SYSCB);
+DECLARE_EXTERNAL_SERVICE(api_metadata, REPLICANT_API_METADATA);
+DECLARE_EXTERNAL_SERVICE(api_filelock, REPLICANT_API_FILELOCK);
diff --git a/Src/replicant/file/ifc_filemetadata_editor.h b/Src/replicant/file/ifc_filemetadata_editor.h
new file mode 100644
index 00000000..e6191634
--- /dev/null
+++ b/Src/replicant/file/ifc_filemetadata_editor.h
@@ -0,0 +1,43 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "metadata/ifc_metadata_editor.h"
+
+class ifc_filemetadata_editor : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_filemetadata_editor() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_filemetadata_editor() {}
+public:
+ ns_error_t GetMetdataObject(ifc_metadata_editor **metadata) { return FileMetadata_GetMetdataObject(metadata); }
+ ns_error_t Save(nx_file_t file) { return FileMetadata_Save(file); }
+ ns_error_t RequireTempFile() { return FileMetadata_RequireTempFile(); }
+ ns_error_t SaveAs(nx_file_t destination, nx_file_t source) { return FileMetadata_SaveAs(destination, source); }
+ void Close() { FileMetadata_Close(); }
+
+ ns_error_t WantID3v2(int *position) { return FileMetadata_WantID3v2(position); }
+ ns_error_t WantID3v1() { return FileMetadata_WantID3v1(); }
+ ns_error_t WantAPEv2(int *position) { return FileMetadata_WantAPEv2(position); }
+ ns_error_t WantLyrics3() { return FileMetadata_WantLyrics3(); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+
+ TAG_POSITION_INDIFFERENT=0,
+ TAG_POSITION_PREPENDED=1,
+ TAG_POSITION_APPENDED=2,
+ };
+protected:
+ virtual ns_error_t WASABICALL FileMetadata_GetMetdataObject(ifc_metadata_editor **metadata)=0;
+ virtual ns_error_t WASABICALL FileMetadata_Save(nx_file_t file) { return NErr_NotImplemented; }
+ virtual ns_error_t WASABICALL FileMetadata_RequireTempFile()=0;
+ virtual ns_error_t WASABICALL FileMetadata_SaveAs(nx_file_t destination, nx_file_t source) { return NErr_NotImplemented; }
+ virtual void WASABICALL FileMetadata_Close() {}
+
+ virtual ns_error_t WASABICALL FileMetadata_WantID3v2(int *position) { return NErr_False; }
+ virtual ns_error_t WASABICALL FileMetadata_WantID3v1() { return NErr_False; }
+ virtual ns_error_t WASABICALL FileMetadata_WantAPEv2(int *position) { return NErr_False; }
+ virtual ns_error_t WASABICALL FileMetadata_WantLyrics3() { return NErr_False; }
+
+};
diff --git a/Src/replicant/file/ifc_fileplayback.h b/Src/replicant/file/ifc_fileplayback.h
new file mode 100644
index 00000000..e384c88f
--- /dev/null
+++ b/Src/replicant/file/ifc_fileplayback.h
@@ -0,0 +1,77 @@
+#pragma once
+#include "audio/ifc_audioout.h"
+#include "foundation/error.h"
+#include "foundation/dispatch.h"
+#include "metadata/ifc_metadata.h"
+#include "player/types.h"
+#include "nx/nxfile.h"
+
+class ifc_fileplayback_parent : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_fileplayback_parent() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_fileplayback_parent() {}
+public:
+ // only call these functions during DecodeStep!
+
+ // if any of these return an error, return it from DecodeStep().
+ // these return NErr_Aborted if there's a seek pending, and NErr_Interrupted if there is an interrupt pending
+ ns_error_t OpenOutput(const ifc_audioout::Parameters *parameters) { return FilePlaybackParent_OpenOutput(parameters); }
+ ns_error_t Output(const void *audio_data, size_t audio_data_length, size_t *frames_consumed, double begin_position_seconds) { return FilePlaybackParent_Output(audio_data, audio_data_length, frames_consumed, begin_position_seconds); }
+ ns_error_t OutputNonInterleaved(const void *audio_data, size_t audio_data_length, size_t *frames_consumed, double begin_position_seconds) { return FilePlaybackParent_OutputNonInterleaved(audio_data, audio_data_length, frames_consumed, begin_position_seconds); }
+ // call this if you have mid-stream metadata updates.
+ ns_error_t OnMetadata(ifc_metadata *new_metadata) { return FilePlaybackParent_OnMetadata(new_metadata); }
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+protected:
+ virtual ns_error_t WASABICALL FilePlaybackParent_OpenOutput(const ifc_audioout::Parameters *parameters)=0;
+ virtual ns_error_t WASABICALL FilePlaybackParent_Output(const void *audio_data, size_t audio_data_length, size_t *frames_consumed, double begin_position_seconds)=0;
+ virtual ns_error_t WASABICALL FilePlaybackParent_OutputNonInterleaved(const void *audio_data, size_t audio_data_length, size_t *frames_consumed, double begin_position_seconds)=0;
+ virtual ns_error_t WASABICALL FilePlaybackParent_OnMetadata(ifc_metadata *new_metadata)=0;
+};
+
+class ifc_fileplayback : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_fileplayback() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_fileplayback() {}
+public:
+ void Close() { FilePlayback_Close(); }
+ ns_error_t Seekable() { return FilePlayback_Seekable(); }
+ ns_error_t GetMetadata(ifc_metadata **metadata) { return FilePlayback_GetMetadata(metadata); }
+ ns_error_t GetLength(double *length, ns_error_t *exact) { return FilePlayback_GetLength(length, exact); }
+ ns_error_t GetBitrate(double *bitrate, ns_error_t *exact) { return FilePlayback_GetBitrate(bitrate, exact); }
+ ns_error_t Seek(const Agave_Seek *seek, ns_error_t *seek_error, double *new_position) { return FilePlayback_Seek(seek, seek_error, new_position); }
+ ns_error_t DecodeStep() { return FilePlayback_DecodeStep(); }
+ ns_error_t Interrupt(Agave_Seek *resume_information) { return FilePlayback_Interrupt(resume_information); }
+ ns_error_t Resume(Agave_Seek *resume_information, nx_file_t file, ifc_metadata *parent_metadata) { return FilePlayback_Resume(resume_information, file, parent_metadata); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+protected:
+ /* you need to handle the possibility that Close gets called more than one time */
+ virtual void WASABICALL FilePlayback_Close()=0;
+ virtual ns_error_t WASABICALL FilePlayback_Seekable()=0;
+ /* implementation note: add a reference (Retain) before assigning the value */
+ virtual ns_error_t WASABICALL FilePlayback_GetMetadata(ifc_metadata **metadata)=0;
+ /* if you set *exact=NErr_False, GetLength will get called after the next DecodeStep */
+ virtual ns_error_t WASABICALL FilePlayback_GetLength(double *length, ns_error_t *exact)=0;
+ virtual ns_error_t WASABICALL FilePlayback_GetBitrate(double *bitrate, ns_error_t *exact)=0;
+ /* only return an error if you're in a state you can't recover from.
+ if you can't seek, then just don't seek and return NErr_Success */
+ virtual ns_error_t WASABICALL FilePlayback_Seek(const Agave_Seek *seek, ns_error_t *seek_error, double *new_position)=0;
+ /* return NErr_Success to continue
+ NErr_EndOfFile to indicate a natural end of file
+ otherwise return an error
+ do _not_ return NErr_Stopped, NErr_Aborted, or NErr_Interrupted, unless they were returned from an ifc_fileplayback_parent function, as these have special meaning */
+ virtual ns_error_t WASABICALL FilePlayback_DecodeStep()=0;
+ /* Save information and close the OS file handle.
+ fill resume_information with whatever information you'll need to resume */
+ virtual ns_error_t WASABICALL FilePlayback_Interrupt(Agave_Seek *resume_information)=0;
+ /* During resume, be sure to call player->SetMetadata again */
+ virtual ns_error_t WASABICALL FilePlayback_Resume(Agave_Seek *resume_information, nx_file_t file, ifc_metadata *parent_metadata)=0;
+};
diff --git a/Src/replicant/file/main.h b/Src/replicant/file/main.h
new file mode 100644
index 00000000..298f5938
--- /dev/null
+++ b/Src/replicant/file/main.h
@@ -0,0 +1,2 @@
+#pragma once
+#include "nx/nxstring.h" \ No newline at end of file
diff --git a/Src/replicant/file/precomp.h b/Src/replicant/file/precomp.h
new file mode 100644
index 00000000..8559c8cf
--- /dev/null
+++ b/Src/replicant/file/precomp.h
@@ -0,0 +1,32 @@
+//
+// precomp.h
+// mp3
+//
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef __cplusplus
+#include "new"
+#endif
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "nu/strsafe.h"
+
+#include "nx/nx.h"
+
+#ifdef __cplusplus
+#include "nu/PtrDeque.h"
+#include "nswasabi/ReferenceCounted.h"
+#include "nswasabi/ServiceName.h"
+#include "service/ifc_servicefactory.h"
+#endif
+
diff --git a/Src/replicant/file/svc_filedecode.h b/Src/replicant/file/svc_filedecode.h
new file mode 100644
index 00000000..ae9b178c
--- /dev/null
+++ b/Src/replicant/file/svc_filedecode.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "audio/parameters.h"
+#include "audio/ifc_audio_decoder_callback.h"
+#include "audio/ifc_audio_decoder_pull.h"
+#include "nx/nxuri.h"
+#include "nx/nxfile.h"
+#include "foundation/error.h"
+#include "decode/svc_decode.h"
+
+// {5F8FD642-9346-4400-803F-A20F342916FD}
+static const GUID filedecode_service_type_guid =
+{ 0x5f8fd642, 0x9346, 0x4400, { 0x80, 0x3f, 0xa2, 0xf, 0x34, 0x29, 0x16, 0xfd } };
+
+class NOVTABLE svc_filedecode : public Wasabi2::Dispatchable
+{
+protected:
+ svc_filedecode() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_filedecode() {}
+
+public:
+ static GUID GetServiceType() { return filedecode_service_type_guid; }
+ ns_error_t CreateAudioDecoder_Callback(ifc_audio_decoder_callback **decoder, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata, nsaudio::Parameters *parameters, int flags) { return FileDecodeService_CreateAudioDecoder_Callback(decoder, filename, file, parent_metadata, parameters, flags); }
+
+enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+protected:
+ virtual ns_error_t WASABICALL FileDecodeService_CreateAudioDecoder_Callback(ifc_audio_decoder_callback **decoder, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata, nsaudio::Parameters *parameters, int flags)=0;
+};
+
diff --git a/Src/replicant/file/svc_filemetadata.h b/Src/replicant/file/svc_filemetadata.h
new file mode 100644
index 00000000..eed7b0d2
--- /dev/null
+++ b/Src/replicant/file/svc_filemetadata.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "foundation/foundation.h"
+#include "nx/nxstring.h"
+#include "ifc_filemetadata_editor.h"
+
+// {92269164-30E1-469A-96C3-96879EF6C39E}
+static const GUID file_metadata_service_type_guid =
+{ 0x92269164, 0x30e1, 0x469a, { 0x96, 0xc3, 0x96, 0x87, 0x9e, 0xf6, 0xc3, 0x9e } };
+
+class svc_filemetadata : public Wasabi2::Dispatchable
+{
+protected:
+ svc_filemetadata() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_filemetadata() {}
+public:
+ static GUID GetServiceType() { return file_metadata_service_type_guid; }
+ // to make the implementation more flexible, you need to NXStringRelease on the extension you get (i.e. this function follows Apple's "Create" rule)
+ int EnumerateExtensions(unsigned int index, nx_string_t *extension) { return FileMetadataService_EnumerateExtensions(index, extension); }
+ int CreateFileMetadata(ifc_metadata **file_metadata, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata) { return FileMetadataService_CreateFileMetadata(file_metadata, filename, file, parent_metadata); }
+ int CreateFileMetadataEditor(ifc_filemetadata_editor **file_metadata, nx_uri_t filename, nx_file_t file, ifc_metadata_editor *parent_metadata) { return FileMetadataService_CreateFileMetadataEditor(file_metadata, filename, file, parent_metadata); }
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ // implementation note: to make the implementation more flexible, you need to NXStringRetain on the extension you pass back (i.e. follow Apple's "Create" rule)
+ virtual int WASABICALL FileMetadataService_EnumerateExtensions(unsigned int index, nx_string_t *extension)=0;
+ virtual int WASABICALL FileMetadataService_CreateFileMetadata(ifc_metadata **file_metadata, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata)=0;
+ virtual int WASABICALL FileMetadataService_CreateFileMetadataEditor(ifc_filemetadata_editor **file_metadata, nx_uri_t filename, nx_file_t file, ifc_metadata_editor *parent_metadata) { return NErr_NotImplemented; }
+};
diff --git a/Src/replicant/file/svc_fileplayback.h b/Src/replicant/file/svc_fileplayback.h
new file mode 100644
index 00000000..ebc7d7a3
--- /dev/null
+++ b/Src/replicant/file/svc_fileplayback.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "nx/nxfile.h"
+#include "foundation/error.h"
+#include "ifc_fileplayback.h"
+#include "metadata/ifc_metadata.h"
+// {AAB6F26D-FF99-4CE8-BC7F-81BEA9F35CA9}
+static const GUID svc_fileplayback_type_guid =
+{ 0xaab6f26d, 0xff99, 0x4ce8, { 0xbc, 0x7f, 0x81, 0xbe, 0xa9, 0xf3, 0x5c, 0xa9 } };
+
+class NOVTABLE svc_fileplayback : public Wasabi2::Dispatchable
+{
+protected:
+ svc_fileplayback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_fileplayback() {}
+
+public:
+ static GUID GetServiceType() { return svc_fileplayback_type_guid; }
+ ns_error_t CreatePlayback(ifc_fileplayback **out_playback_object, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata, ifc_fileplayback_parent *parent) { return FilePlaybackService_CreatePlayback(out_playback_object, filename, file, parent_metadata, parent); }
+
+enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+protected:
+ /* do _not_ retain/release the ifc_fileplayback_parent object! */
+ virtual ns_error_t WASABICALL FilePlaybackService_CreatePlayback(ifc_fileplayback **out_playback_object, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata, ifc_fileplayback_parent *parent)=0;
+};
+
diff --git a/Src/replicant/file/svc_filerawreader.h b/Src/replicant/file/svc_filerawreader.h
new file mode 100644
index 00000000..29146cc4
--- /dev/null
+++ b/Src/replicant/file/svc_filerawreader.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "nx/nxfile.h"
+#include "foundation/error.h"
+#include "decode/ifc_raw_media_reader.h"
+#include "metadata/ifc_metadata.h"
+
+// {FF84B47B-5ED4-45E8-B822-0F8FE20F80A8}
+static const GUID filerawreader_service_type_guid =
+{ 0xff84b47b, 0x5ed4, 0x45e8, { 0xb8, 0x22, 0xf, 0x8f, 0xe2, 0xf, 0x80, 0xa8 } };
+
+class NOVTABLE svc_filerawreader : public Wasabi2::Dispatchable
+{
+protected:
+ svc_filerawreader() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_filerawreader() {}
+
+public:
+ static GUID GetServiceType() { return filerawreader_service_type_guid; }
+ int CreateRawMediaReader(ifc_raw_media_reader **reader, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata) { return FileRawReaderService_CreateRawMediaReader(reader, filename, file, parent_metadata); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+protected:
+ virtual ns_error_t WASABICALL FileRawReaderService_CreateRawMediaReader(ifc_raw_media_reader **reader, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata)=0;
+};
+