summaryrefslogtreecommitdiffstats
path: root/Src/replicant
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 14:54:57 +0200
committerJef <jef@targetspot.com>2024-09-24 14:54:57 +0200
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant
parentAdding .gitignore (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar
winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.bz2
winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.lz
winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.xz
winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.zst
winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.zip
Diffstat (limited to 'Src/replicant')
-rw-r--r--Src/replicant/Wasabi/Android.mk15
-rw-r--r--Src/replicant/Wasabi/Makefile42
-rw-r--r--Src/replicant/Wasabi/ServiceManager.cpp201
-rw-r--r--Src/replicant/Wasabi/ServiceManager.h52
-rw-r--r--Src/replicant/Wasabi/SysCallbacks.cpp98
-rw-r--r--Src/replicant/Wasabi/SysCallbacks.h31
-rw-r--r--Src/replicant/Wasabi/VERSION1
-rw-r--r--Src/replicant/Wasabi/Wasabi-replicant.sln31
-rw-r--r--Src/replicant/Wasabi/Wasabi-replicant.vcxproj207
-rw-r--r--Src/replicant/Wasabi/Wasabi-replicant.vcxproj.filters42
-rw-r--r--Src/replicant/Wasabi/Wasabi.h10
-rw-r--r--Src/replicant/Wasabi/Wasabi.vcxproj143
-rw-r--r--Src/replicant/Wasabi/Wasabi.xcodeproj/project.pbxproj457
-rw-r--r--Src/replicant/Wasabi/Wasabi.xcodeproj/xcshareddata/xcschemes/Wasabi.xcscheme59
-rw-r--r--Src/replicant/Wasabi/api.cpp15
-rw-r--r--Src/replicant/Wasabi/api__wasabi-replicant.h7
-rw-r--r--Src/replicant/Wasabi/precomp.h11
-rw-r--r--Src/replicant/application/api_application.h62
-rw-r--r--Src/replicant/application/features.h92
-rw-r--r--Src/replicant/audio/ifc_audio_decoder_callback.h57
-rw-r--r--Src/replicant/audio/ifc_audio_decoder_packet.h23
-rw-r--r--Src/replicant/audio/ifc_audio_decoder_pull.h34
-rw-r--r--Src/replicant/audio/ifc_audioout.h76
-rw-r--r--Src/replicant/audio/ifc_equalizer.h27
-rw-r--r--Src/replicant/audio/parameters.h47
-rw-r--r--Src/replicant/audio/types.h44
-rw-r--r--Src/replicant/component/ComponentManagerBase.cpp224
-rw-r--r--Src/replicant/component/ComponentManagerBase.h43
-rw-r--r--Src/replicant/component/ifc_component.h95
-rw-r--r--Src/replicant/component/ifc_component_sync.h22
-rw-r--r--Src/replicant/component/win/ComponentManager.cpp88
-rw-r--r--Src/replicant/component/win/ComponentManager.h12
-rw-r--r--Src/replicant/decode/api_decode.h35
-rw-r--r--Src/replicant/decode/ifc_raw_media_reader.h20
-rw-r--r--Src/replicant/decode/svc_decode.h80
-rw-r--r--Src/replicant/decode/svc_raw_media_reader.h27
-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
-rw-r--r--Src/replicant/filelock/api_filelock.h49
-rw-r--r--Src/replicant/foundation/align.h9
-rw-r--r--Src/replicant/foundation/atomics.h20
-rw-r--r--Src/replicant/foundation/dispatch.h74
-rw-r--r--Src/replicant/foundation/error.h88
-rw-r--r--Src/replicant/foundation/export.h18
-rw-r--r--Src/replicant/foundation/foundation.h7
-rw-r--r--Src/replicant/foundation/guid.h35
-rw-r--r--Src/replicant/foundation/mkncc.h12
-rw-r--r--Src/replicant/foundation/types.h16
-rw-r--r--Src/replicant/foundation/win-amd64/atomics.h104
-rw-r--r--Src/replicant/foundation/win-amd64/types.h92
-rw-r--r--Src/replicant/foundation/win-x86/atomics.h61
-rw-r--r--Src/replicant/foundation/win-x86/types.h98
-rw-r--r--Src/replicant/http/HTTPPlayback.cpp325
-rw-r--r--Src/replicant/http/HTTPPlayback.h37
-rw-r--r--Src/replicant/http/HTTPPlaybackService.cpp28
-rw-r--r--Src/replicant/http/HTTPPlaybackService.h16
-rw-r--r--Src/replicant/http/api.h8
-rw-r--r--Src/replicant/http/http.vcxproj156
-rw-r--r--Src/replicant/http/ifc_http.h68
-rw-r--r--Src/replicant/http/ifc_http_demuxer.h25
-rw-r--r--Src/replicant/http/main.cpp68
-rw-r--r--Src/replicant/http/svc_http_demuxer.h38
-rw-r--r--Src/replicant/icy/ifc_icy_playback.h26
-rw-r--r--Src/replicant/icy/ifc_icy_reader.h37
-rw-r--r--Src/replicant/icy/svc_icy_playback.h31
-rw-r--r--Src/replicant/jnetlib/VERSION1
-rw-r--r--Src/replicant/jnetlib/asyncdns.cpp314
-rw-r--r--Src/replicant/jnetlib/asyncdns.h66
-rw-r--r--Src/replicant/jnetlib/connection.cpp533
-rw-r--r--Src/replicant/jnetlib/connection.h168
-rw-r--r--Src/replicant/jnetlib/headers.cpp119
-rw-r--r--Src/replicant/jnetlib/headers.h20
-rw-r--r--Src/replicant/jnetlib/httpget.cpp845
-rw-r--r--Src/replicant/jnetlib/httpget.h139
-rw-r--r--Src/replicant/jnetlib/httpserv.cpp234
-rw-r--r--Src/replicant/jnetlib/httpserv.h71
-rw-r--r--Src/replicant/jnetlib/httpuserv.cpp216
-rw-r--r--Src/replicant/jnetlib/httpuserv.h55
-rw-r--r--Src/replicant/jnetlib/jnetlib-replicant.rc76
-rw-r--r--Src/replicant/jnetlib/jnetlib-replicant.vcproj491
-rw-r--r--Src/replicant/jnetlib/jnetlib.cpp630
-rw-r--r--Src/replicant/jnetlib/jnetlib.h177
-rw-r--r--Src/replicant/jnetlib/jnetlib.sln54
-rw-r--r--Src/replicant/jnetlib/jnetlib.vcxproj281
-rw-r--r--Src/replicant/jnetlib/jnetlib.vcxproj.filters137
-rw-r--r--Src/replicant/jnetlib/jnetlib_defines.h66
-rw-r--r--Src/replicant/jnetlib/listen.cpp131
-rw-r--r--Src/replicant/jnetlib/listen.h44
-rw-r--r--Src/replicant/jnetlib/multicastlisten.cpp123
-rw-r--r--Src/replicant/jnetlib/multicastlisten.h26
-rw-r--r--Src/replicant/jnetlib/netinc.h84
-rw-r--r--Src/replicant/jnetlib/resource.h14
-rw-r--r--Src/replicant/jnetlib/sslconnection.cpp329
-rw-r--r--Src/replicant/jnetlib/sslconnection.h61
-rw-r--r--Src/replicant/jnetlib/udpconnection.cpp384
-rw-r--r--Src/replicant/jnetlib/udpconnection.h151
-rw-r--r--Src/replicant/jnetlib/util.cpp185
-rw-r--r--Src/replicant/jnetlib/util.h40
-rw-r--r--Src/replicant/jnetlib/version.rc239
-rw-r--r--Src/replicant/metadata/MetadataKeys.h50
-rw-r--r--Src/replicant/metadata/api_artwork.h35
-rw-r--r--Src/replicant/metadata/api_metadata.h48
-rw-r--r--Src/replicant/metadata/genres.h41
-rw-r--r--Src/replicant/metadata/ifc_metadata.h65
-rw-r--r--Src/replicant/metadata/ifc_metadata_editor.h43
-rw-r--r--Src/replicant/metadata/metadata.h7
-rw-r--r--Src/replicant/metadata/metadata.xcodeproj/project.pbxproj268
-rw-r--r--Src/replicant/metadata/metadata.xcodeproj/xcshareddata/xcschemes/metadata.xcscheme59
-rw-r--r--Src/replicant/metadata/svc_metadata.h65
-rw-r--r--Src/replicant/metadata/types.h34
-rw-r--r--Src/replicant/mp4/MP4Decoder.h24
-rw-r--r--Src/replicant/mp4/MP4DecoderCallback.h35
-rw-r--r--Src/replicant/mp4/MP4FileObject.h33
-rw-r--r--Src/replicant/mp4/MP4HTTP.h44
-rw-r--r--Src/replicant/mp4/MP4Metadata.h14
-rw-r--r--Src/replicant/mp4/MP4MetadataBase.h31
-rw-r--r--Src/replicant/mp4/MP4MetadataEditor.h24
-rw-r--r--Src/replicant/mp4/MP4MetadataFile.h15
-rw-r--r--Src/replicant/mp4/MP4MetadataService.h48
-rw-r--r--Src/replicant/mp4/MP4Playback.h39
-rw-r--r--Src/replicant/mp4/MP4PlaybackService.h18
-rw-r--r--Src/replicant/mp4/MP4RawReader.h36
-rw-r--r--Src/replicant/mp4/api.h16
-rw-r--r--Src/replicant/mp4/ifc_mp4audiodecoder.h32
-rw-r--r--Src/replicant/mp4/ifc_mp4file.h72
-rw-r--r--Src/replicant/mp4/main.h18
-rw-r--r--Src/replicant/mp4/precomp.h32
-rw-r--r--Src/replicant/mp4/svc_mp4decoder.h27
-rw-r--r--Src/replicant/nsaac/ADTSHeader.c116
-rw-r--r--Src/replicant/nsaac/ADTSHeader.h35
-rw-r--r--Src/replicant/nsapev2/VERSION1
-rw-r--r--Src/replicant/nsapev2/flags.h20
-rw-r--r--Src/replicant/nsapev2/header.cpp91
-rw-r--r--Src/replicant/nsapev2/header.h39
-rw-r--r--Src/replicant/nsapev2/item.cpp235
-rw-r--r--Src/replicant/nsapev2/item.h43
-rw-r--r--Src/replicant/nsapev2/nsapev2.h75
-rw-r--r--Src/replicant/nsapev2/nsapev2.sln81
-rw-r--r--Src/replicant/nsapev2/nsapev2.vcxproj172
-rw-r--r--Src/replicant/nsapev2/nsapev2_common.cpp465
-rw-r--r--Src/replicant/nsapev2/precomp.h26
-rw-r--r--Src/replicant/nsapev2/tag.cpp285
-rw-r--r--Src/replicant/nsapev2/tag.h50
-rw-r--r--Src/replicant/nsapev2/util.h1
-rw-r--r--Src/replicant/nsapev2/windows/nsapev2.cpp1
-rw-r--r--Src/replicant/nsapev2/windows/nsapev2.h20
-rw-r--r--Src/replicant/nsid3v1/VERSION1
-rw-r--r--Src/replicant/nsid3v1/nsid3v1.cpp350
-rw-r--r--Src/replicant/nsid3v1/nsid3v1.h67
-rw-r--r--Src/replicant/nsid3v1/nsid3v1.sln44
-rw-r--r--Src/replicant/nsid3v1/nsid3v1.vcxproj171
-rw-r--r--Src/replicant/nsid3v1/precomp.h18
-rw-r--r--Src/replicant/nsid3v1/tag.cpp177
-rw-r--r--Src/replicant/nsid3v1/tag.h54
-rw-r--r--Src/replicant/nsid3v2/extendedheader.cpp108
-rw-r--r--Src/replicant/nsid3v2/extendedheader.h52
-rw-r--r--Src/replicant/nsid3v2/frame.cpp786
-rw-r--r--Src/replicant/nsid3v2/frame.h117
-rw-r--r--Src/replicant/nsid3v2/frame_apic.cpp247
-rw-r--r--Src/replicant/nsid3v2/frame_comments.cpp185
-rw-r--r--Src/replicant/nsid3v2/frame_id.cpp167
-rw-r--r--Src/replicant/nsid3v2/frame_object.cpp86
-rw-r--r--Src/replicant/nsid3v2/frame_popm.cpp95
-rw-r--r--Src/replicant/nsid3v2/frame_private.cpp61
-rw-r--r--Src/replicant/nsid3v2/frame_text.cpp95
-rw-r--r--Src/replicant/nsid3v2/frame_url.cpp42
-rw-r--r--Src/replicant/nsid3v2/frame_usertext.cpp166
-rw-r--r--Src/replicant/nsid3v2/frame_userurl.cpp78
-rw-r--r--Src/replicant/nsid3v2/frame_utils.cpp265
-rw-r--r--Src/replicant/nsid3v2/frame_utils.h21
-rw-r--r--Src/replicant/nsid3v2/frameheader.cpp403
-rw-r--r--Src/replicant/nsid3v2/frameheader.h124
-rw-r--r--Src/replicant/nsid3v2/frames.c61
-rw-r--r--Src/replicant/nsid3v2/frames.h21
-rw-r--r--Src/replicant/nsid3v2/header.cpp186
-rw-r--r--Src/replicant/nsid3v2/header.h44
-rw-r--r--Src/replicant/nsid3v2/nsid3v2.h190
-rw-r--r--Src/replicant/nsid3v2/nsid3v2.sln44
-rw-r--r--Src/replicant/nsid3v2/nsid3v2.vcxproj194
-rw-r--r--Src/replicant/nsid3v2/nsid3v2_common.cpp366
-rw-r--r--Src/replicant/nsid3v2/precomp.h32
-rw-r--r--Src/replicant/nsid3v2/tag.cpp644
-rw-r--r--Src/replicant/nsid3v2/tag.h106
-rw-r--r--Src/replicant/nsid3v2/util.cpp161
-rw-r--r--Src/replicant/nsid3v2/util.h28
-rw-r--r--Src/replicant/nsid3v2/values.cpp49
-rw-r--r--Src/replicant/nsid3v2/values.h22
-rw-r--r--Src/replicant/nsid3v2/windows/frame_apic.cpp170
-rw-r--r--Src/replicant/nsid3v2/windows/frame_comments.cpp228
-rw-r--r--Src/replicant/nsid3v2/windows/nsid3v2.cpp73
-rw-r--r--Src/replicant/nsid3v2/windows/nsid3v2.h20
-rw-r--r--Src/replicant/nsmp3/LAMEInfo.cpp293
-rw-r--r--Src/replicant/nsmp3/LAMEInfo.h66
-rw-r--r--Src/replicant/nsmp3/MPEGHeader.cpp170
-rw-r--r--Src/replicant/nsmp3/MPEGHeader.h47
-rw-r--r--Src/replicant/nsmp3/OFL.cpp165
-rw-r--r--Src/replicant/nsmp3/OFL.h20
-rw-r--r--Src/replicant/nsmp3/nsmp3.sln31
-rw-r--r--Src/replicant/nsmp3/nsmp3.vcxproj159
-rw-r--r--Src/replicant/nsmp3dec/bitsequence.h69
-rw-r--r--Src/replicant/nsmp3dec/bitstream.h103
-rw-r--r--Src/replicant/nsmp3dec/conceal.h137
-rw-r--r--Src/replicant/nsmp3dec/crc16.h35
-rw-r--r--Src/replicant/nsmp3dec/giobase.h46
-rw-r--r--Src/replicant/nsmp3dec/huffdec.h57
-rw-r--r--Src/replicant/nsmp3dec/huffmanbitobj.h54
-rw-r--r--Src/replicant/nsmp3dec/huffmandecoder.h85
-rw-r--r--Src/replicant/nsmp3dec/huffmantable.h102
-rw-r--r--Src/replicant/nsmp3dec/l3reg.h112
-rw-r--r--Src/replicant/nsmp3dec/l3table.h47
-rw-r--r--Src/replicant/nsmp3dec/mdct.h61
-rw-r--r--Src/replicant/nsmp3dec/meanvalue.h61
-rw-r--r--Src/replicant/nsmp3dec/mp2decode.h50
-rw-r--r--Src/replicant/nsmp3dec/mp3ancofl.h86
-rw-r--r--Src/replicant/nsmp3dec/mp3decode.h113
-rw-r--r--Src/replicant/nsmp3dec/mp3drmifc.h25
-rw-r--r--Src/replicant/nsmp3dec/mp3quant.h43
-rw-r--r--Src/replicant/nsmp3dec/mp3read.h60
-rw-r--r--Src/replicant/nsmp3dec/mp3ssc.h57
-rw-r--r--Src/replicant/nsmp3dec/mp3sscdef.h154
-rw-r--r--Src/replicant/nsmp3dec/mp3streaminfo.h117
-rw-r--r--Src/replicant/nsmp3dec/mp3tools.h66
-rw-r--r--Src/replicant/nsmp3dec/mpeg.h174
-rw-r--r--Src/replicant/nsmp3dec/mpegbitstream.h71
-rw-r--r--Src/replicant/nsmp3dec/mpegheader.h105
-rw-r--r--Src/replicant/nsmp3dec/mpgadecoder.h100
-rw-r--r--Src/replicant/nsmp3dec/polyphase.h61
-rw-r--r--Src/replicant/nsmp3dec/precomp.h19
-rw-r--r--Src/replicant/nsmp3dec/regtypes.h66
-rw-r--r--Src/replicant/nsmp3dec/sequencedetector.h53
-rw-r--r--Src/replicant/nswasabi/APEv2Metadata.cpp306
-rw-r--r--Src/replicant/nswasabi/APEv2Metadata.h29
-rw-r--r--Src/replicant/nswasabi/Android.mk12
-rw-r--r--Src/replicant/nswasabi/ApplicationBase.cpp168
-rw-r--r--Src/replicant/nswasabi/ApplicationBase.h44
-rw-r--r--Src/replicant/nswasabi/AutoCharNX.h184
-rw-r--r--Src/replicant/nswasabi/ComponentManagerBase.cpp145
-rw-r--r--Src/replicant/nswasabi/ComponentManagerBase.h31
-rw-r--r--Src/replicant/nswasabi/ID3v1Metadata.cpp187
-rw-r--r--Src/replicant/nswasabi/ID3v1Metadata.h26
-rw-r--r--Src/replicant/nswasabi/ID3v2Metadata.cpp1087
-rw-r--r--Src/replicant/nswasabi/ID3v2Metadata.h34
-rw-r--r--Src/replicant/nswasabi/Makefile48
-rw-r--r--Src/replicant/nswasabi/MetadataChain.h97
-rw-r--r--Src/replicant/nswasabi/MetadataEditorChain.h100
-rw-r--r--Src/replicant/nswasabi/ObjectFactory.h57
-rw-r--r--Src/replicant/nswasabi/PlaybackBase.cpp409
-rw-r--r--Src/replicant/nswasabi/PlaybackBase.h91
-rw-r--r--Src/replicant/nswasabi/PlaybackBase2.cpp490
-rw-r--r--Src/replicant/nswasabi/PlaybackBase2.h78
-rw-r--r--Src/replicant/nswasabi/ReferenceCounted.h232
-rw-r--r--Src/replicant/nswasabi/ServiceName.h21
-rw-r--r--Src/replicant/nswasabi/VERSION1
-rw-r--r--Src/replicant/nswasabi/XMLString.cpp44
-rw-r--r--Src/replicant/nswasabi/XMLString.h21
-rw-r--r--Src/replicant/nswasabi/nswasabi.sln82
-rw-r--r--Src/replicant/nswasabi/nswasabi.vcxproj159
-rw-r--r--Src/replicant/nswasabi/nswasabi.xcodeproj/project.pbxproj499
-rw-r--r--Src/replicant/nswasabi/nswasabi.xcodeproj/xcshareddata/xcschemes/nswasabi.xcscheme59
-rw-r--r--Src/replicant/nswasabi/precomp.h32
-rw-r--r--Src/replicant/nswasabi/singleton.h117
-rw-r--r--Src/replicant/nu/AutoBuffer.h49
-rw-r--r--Src/replicant/nu/AutoChar.h141
-rw-r--r--Src/replicant/nu/AutoLock.h377
-rw-r--r--Src/replicant/nu/AutoWide.h50
-rw-r--r--Src/replicant/nu/Benchmark.h44
-rw-r--r--Src/replicant/nu/BitReader.cpp109
-rw-r--r--Src/replicant/nu/BitReader.h18
-rw-r--r--Src/replicant/nu/ByteReader.c203
-rw-r--r--Src/replicant/nu/ByteReader.h83
-rw-r--r--Src/replicant/nu/ByteWriter.c77
-rw-r--r--Src/replicant/nu/ByteWriter.h48
-rw-r--r--Src/replicant/nu/LockFreeFIFO.cpp110
-rw-r--r--Src/replicant/nu/LockFreeFIFO.h34
-rw-r--r--Src/replicant/nu/LockFreeItem.h35
-rw-r--r--Src/replicant/nu/LockFreeLIFO.c61
-rw-r--r--Src/replicant/nu/LockFreeLIFO.h18
-rw-r--r--Src/replicant/nu/LockFreeRingBuffer.cpp421
-rw-r--r--Src/replicant/nu/LockFreeRingBuffer.h52
-rw-r--r--Src/replicant/nu/MessageLoop.h16
-rw-r--r--Src/replicant/nu/Pairs.h15
-rw-r--r--Src/replicant/nu/ProgressTracker.cpp117
-rw-r--r--Src/replicant/nu/ProgressTracker.h24
-rw-r--r--Src/replicant/nu/RingBuffer.cpp464
-rw-r--r--Src/replicant/nu/RingBuffer.h61
-rw-r--r--Src/replicant/nu/SafeSize.h51
-rw-r--r--Src/replicant/nu/ThreadLoop.h16
-rw-r--r--Src/replicant/nu/lfitem.c25
-rw-r--r--Src/replicant/nu/lfitem.h23
-rw-r--r--Src/replicant/nu/lfmpscq.c50
-rw-r--r--Src/replicant/nu/lfmpscq.h23
-rw-r--r--Src/replicant/nu/lfringbuffer.c158
-rw-r--r--Src/replicant/nu/lfringbuffer.h46
-rw-r--r--Src/replicant/nu/nodelist.c71
-rw-r--r--Src/replicant/nu/nodelist.h24
-rw-r--r--Src/replicant/nu/ns_wc.h51
-rw-r--r--Src/replicant/nu/nu.sln28
-rw-r--r--Src/replicant/nu/nu.vcxproj267
-rw-r--r--Src/replicant/nu/nu.vcxproj.filters107
-rw-r--r--Src/replicant/nu/precomp.h18
-rw-r--r--Src/replicant/nu/queue_node.h31
-rw-r--r--Src/replicant/nu/strsafe.h4497
-rw-r--r--Src/replicant/nu/utf.c649
-rw-r--r--Src/replicant/nu/utf.h26
-rw-r--r--Src/replicant/nu/win-amd64/LockFreeLIFO.h34
-rw-r--r--Src/replicant/nu/win-amd64/ThreadLoop.cpp70
-rw-r--r--Src/replicant/nu/win-amd64/ThreadLoop.h38
-rw-r--r--Src/replicant/nu/win-x86/LockFreeLIFO.asm50
-rw-r--r--Src/replicant/nu/win-x86/LockFreeLIFO.c48
-rw-r--r--Src/replicant/nu/win-x86/LockFreeLIFO.h31
-rw-r--r--Src/replicant/nu/win-x86/ThreadLoop.cpp84
-rw-r--r--Src/replicant/nu/win-x86/ThreadLoop.h37
-rw-r--r--Src/replicant/nu/win/MessageLoop.cpp121
-rw-r--r--Src/replicant/nu/win/MessageLoop.h45
-rw-r--r--Src/replicant/nu/win/ThreadLoop.cpp146
-rw-r--r--Src/replicant/nu/win/ThreadLoop.h40
-rw-r--r--Src/replicant/nu/x86/ByteWriter.h85
-rw-r--r--Src/replicant/nx/Android.mk27
-rw-r--r--Src/replicant/nx/Makefile89
-rw-r--r--Src/replicant/nx/VERSION1
-rw-r--r--Src/replicant/nx/cpufeatures/Android.mk7
-rw-r--r--Src/replicant/nx/cpufeatures/cpu-features.c432
-rw-r--r--Src/replicant/nx/cpufeatures/cpu-features.h70
-rw-r--r--Src/replicant/nx/nx.h14
-rw-r--r--Src/replicant/nx/nx.rc76
-rw-r--r--Src/replicant/nx/nx.sln67
-rw-r--r--Src/replicant/nx/nx.vcxproj290
-rw-r--r--Src/replicant/nx/nx.vcxproj.filters80
-rw-r--r--Src/replicant/nx/nx.xcodeproj/project.pbxproj802
-rw-r--r--Src/replicant/nx/nx.xcodeproj/xcshareddata/xcschemes/nx.xcscheme59
-rw-r--r--Src/replicant/nx/nx.xcworkspace/contents.xcworkspacedata16
-rw-r--r--Src/replicant/nx/nx.xcworkspace/xcshareddata/xcschemes/nx-complete.xcscheme87
-rw-r--r--Src/replicant/nx/nxapi.h12
-rw-r--r--Src/replicant/nx/nxcondition.h10
-rw-r--r--Src/replicant/nx/nxdata.h10
-rw-r--r--Src/replicant/nx/nxfile.h10
-rw-r--r--Src/replicant/nx/nximage.h4
-rw-r--r--Src/replicant/nx/nxlog.h10
-rw-r--r--Src/replicant/nx/nxmutablestring.h10
-rw-r--r--Src/replicant/nx/nxonce.h14
-rw-r--r--Src/replicant/nx/nxpath.h8
-rw-r--r--Src/replicant/nx/nxsemaphore.h10
-rw-r--r--Src/replicant/nx/nxsleep.h8
-rw-r--r--Src/replicant/nx/nxstring.h10
-rw-r--r--Src/replicant/nx/nxthread.h8
-rw-r--r--Src/replicant/nx/nxtime.h10
-rw-r--r--Src/replicant/nx/nxuri.h10
-rw-r--r--Src/replicant/nx/resource.h14
-rw-r--r--Src/replicant/nx/version.rc239
-rw-r--r--Src/replicant/nx/win/NXFileObject.cpp525
-rw-r--r--Src/replicant/nx/win/NXFileObject.h45
-rw-r--r--Src/replicant/nx/win/NXFileProgressiveDownloader.cpp749
-rw-r--r--Src/replicant/nx/win/NXZipFile.cpp209
-rw-r--r--Src/replicant/nx/win/nxapi.h15
-rw-r--r--Src/replicant/nx/win/nxcondition.c66
-rw-r--r--Src/replicant/nx/win/nxcondition.h28
-rw-r--r--Src/replicant/nx/win/nxdata.c276
-rw-r--r--Src/replicant/nx/win/nxdata.h42
-rw-r--r--Src/replicant/nx/win/nxfile.c68
-rw-r--r--Src/replicant/nx/win/nxfile.h116
-rw-r--r--Src/replicant/nx/win/nximage.c34
-rw-r--r--Src/replicant/nx/win/nximage.h24
-rw-r--r--Src/replicant/nx/win/nxlog.c19
-rw-r--r--Src/replicant/nx/win/nxlog.h13
-rw-r--r--Src/replicant/nx/win/nxmutablestring.c40
-rw-r--r--Src/replicant/nx/win/nxmutablestring.h24
-rw-r--r--Src/replicant/nx/win/nxonce.c43
-rw-r--r--Src/replicant/nx/win/nxonce.h35
-rw-r--r--Src/replicant/nx/win/nxpath.c113
-rw-r--r--Src/replicant/nx/win/nxpath.h26
-rw-r--r--Src/replicant/nx/win/nxsemaphore.c24
-rw-r--r--Src/replicant/nx/win/nxsemaphore.h16
-rw-r--r--Src/replicant/nx/win/nxsleep.c14
-rw-r--r--Src/replicant/nx/win/nxsleep.h17
-rw-r--r--Src/replicant/nx/win/nxstring.c756
-rw-r--r--Src/replicant/nx/win/nxstring.h95
-rw-r--r--Src/replicant/nx/win/nxthread.c25
-rw-r--r--Src/replicant/nx/win/nxthread.h32
-rw-r--r--Src/replicant/nx/win/nxtime.h10
-rw-r--r--Src/replicant/nx/win/nxuri.c258
-rw-r--r--Src/replicant/nx/win/nxuri.h44
-rw-r--r--Src/replicant/ogg/api.h8
-rw-r--r--Src/replicant/ogg/ifc_oggaudiodecoder.h16
-rw-r--r--Src/replicant/ogg/svc_oggdecoder.h26
-rw-r--r--Src/replicant/player/ifc_playback.h71
-rw-r--r--Src/replicant/player/ifc_playback_parameters.h20
-rw-r--r--Src/replicant/player/ifc_player.h93
-rw-r--r--Src/replicant/player/svc_output.h40
-rw-r--r--Src/replicant/player/types.h44
-rw-r--r--Src/replicant/replaygain/ifc_replaygain_settings.h62
-rw-r--r--Src/replicant/replicant/metadata/Android.mk15
-rw-r--r--Src/replicant/replicant/metadata/ArtworkManager.cpp750
-rw-r--r--Src/replicant/replicant/metadata/ArtworkManager.h52
-rw-r--r--Src/replicant/replicant/metadata/Makefile53
-rw-r--r--Src/replicant/replicant/metadata/MetadataManager.cpp358
-rw-r--r--Src/replicant/replicant/metadata/MetadataManager.h44
-rw-r--r--Src/replicant/replicant/metadata/VERSION1
-rw-r--r--Src/replicant/replicant/metadata/api__wasabi2_metadata.h9
-rw-r--r--Src/replicant/replicant/metadata/metadata.cpp39
-rw-r--r--Src/replicant/replicant/metadata/metadata.h11
-rw-r--r--Src/replicant/replicant/metadata/metadata.sln29
-rw-r--r--Src/replicant/replicant/metadata/metadata.vcxproj208
-rw-r--r--Src/replicant/replicant/metadata/metadata.vcxproj.filters39
-rw-r--r--Src/replicant/replicant/metadata/precomp.h12
-rw-r--r--Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/project.pbxproj457
-rw-r--r--Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/xcshareddata/xcschemes/replicant-metadata.xcscheme59
-rw-r--r--Src/replicant/replicant/version.h9
-rw-r--r--Src/replicant/service/api_service.h63
-rw-r--r--Src/replicant/service/ifc_servicefactory.h44
-rw-r--r--Src/replicant/service/svccb.h38
-rw-r--r--Src/replicant/service/types.h5
-rw-r--r--Src/replicant/ssdp/Android.mk18
-rw-r--r--Src/replicant/ssdp/SSDPAPI.cpp436
-rw-r--r--Src/replicant/ssdp/SSDPAPI.h61
-rw-r--r--Src/replicant/ssdp/api__ssdp.h20
-rw-r--r--Src/replicant/ssdp/api_ssdp.h32
-rw-r--r--Src/replicant/ssdp/cb_ssdp.h21
-rw-r--r--Src/replicant/ssdp/main.cpp88
-rw-r--r--Src/replicant/ssdp/main.h1
-rw-r--r--Src/replicant/ssdp/resource.h14
-rw-r--r--Src/replicant/ssdp/ssdp.rc76
-rw-r--r--Src/replicant/ssdp/ssdp.vcxproj302
-rw-r--r--Src/replicant/ssdp/ssdp.vcxproj.filters49
-rw-r--r--Src/replicant/ssdp/version.rc239
-rw-r--r--Src/replicant/syscb/api_syscb.h51
-rw-r--r--Src/replicant/syscb/ifc_syscallback.h26
-rw-r--r--Src/replicant/ultravox/ifc_ultravox_playback.h26
-rw-r--r--Src/replicant/ultravox/ifc_ultravox_reader.h28
-rw-r--r--Src/replicant/ultravox/svc_ultravox_playback.h27
440 files changed, 48470 insertions, 0 deletions
diff --git a/Src/replicant/Wasabi/Android.mk b/Src/replicant/Wasabi/Android.mk
new file mode 100644
index 000000000..39ddff2ac
--- /dev/null
+++ b/Src/replicant/Wasabi/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := Wasabi
+LOCAL_C_INCLUDES := $(ROOT_REPLICANT)
+
+LOCAL_SRC_FILES := api.cpp ServiceManager.cpp SysCallbacks.cpp
+
+
+LOCAL_STATIC_LIBRARIES := nu foundation
+LOCAL_SHARED_LIBRARIES := nx
+
+include $(BUILD_STATIC_LIBRARY)
+
+
diff --git a/Src/replicant/Wasabi/Makefile b/Src/replicant/Wasabi/Makefile
new file mode 100644
index 000000000..444701b53
--- /dev/null
+++ b/Src/replicant/Wasabi/Makefile
@@ -0,0 +1,42 @@
+MODULE_NAME := Wasabi
+
+CPPSOURCES := api.cpp ServiceManager.cpp SysCallbacks.cpp
+
+LIBRARY_FILENAME := lib$(MODULE_NAME).a
+OUTPUT_PATH := ../build/$(MODULE_NAME)
+ARCHIVE_PATH := ../build/lib
+LIBRARY_FILEPATH := ../build/lib/$(LIBRARY_FILENAME)
+
+CPPOBJS := $(patsubst %.cpp,$(OUTPUT_PATH)/%.o,$(CPPSOURCES))
+CPPDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(CPPOBJS))
+COBJS := $(patsubst %.c,$(OUTPUT_PATH)/%.o,$(CSOURCES))
+CDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(COBJS))
+
+OBJS := $(CPPOBJS) $(COBJS)
+DEPS := $(CPPDEPS) $(CDEPS)
+
+CFLAGS=-I..
+CPPFLAGS := ${CFLAGS}
+
+
+
+build: build-dir $(LIBRARY_FILEPATH)
+
+build-dir:
+ @mkdir -p $(OUTPUT_PATH) > /dev/null 2> /dev/null
+ @mkdir -p $(ARCHIVE_PATH) > /dev/null 2> /dev/null
+
+dep:
+ @rm ${DEPS}
+
+$(OUTPUT_PATH)/%.o: %.cpp
+ #@echo Compiling $*.cpp
+ @$(CXX) $(CPPFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.cpp -o $(OUTPUT_PATH)/$*.o
+
+$(LIBRARY_FILEPATH): ${OBJS}
+ $(AR) rcs $@ ${OBJS}
+
+clean:
+ -rm -f ${OBJS} $(LIBRARY_FILENAME) ${DEPS}
+
+-include $(DEPS)
diff --git a/Src/replicant/Wasabi/ServiceManager.cpp b/Src/replicant/Wasabi/ServiceManager.cpp
new file mode 100644
index 000000000..2d6887aa1
--- /dev/null
+++ b/Src/replicant/Wasabi/ServiceManager.cpp
@@ -0,0 +1,201 @@
+#include "ServiceManager.h"
+#include "api__wasabi-replicant.h"
+#include "service/ifc_servicefactory.h"
+#include "service/svccb.h"
+
+using namespace nu;
+
+ServiceManager::ServiceManager()
+{
+#ifdef _WIN32
+ component_wait = CreateSemaphoreW(NULL, 0, LONG_MAX, NULL);
+#else
+ sem_init(&component_wait, 0, 0);
+#endif
+
+}
+
+ServiceManager::~ServiceManager()
+{
+ #ifdef _WIN32
+ if (component_wait)
+ CloseHandle(component_wait);
+#else
+ sem_destroy(&component_wait);
+#endif
+}
+
+int ServiceManager::Dispatchable_QueryInterface(GUID interface_guid, void **object)
+{
+ if (interface_guid == ifc_component_sync::GetInterfaceGUID())
+ {
+ *object = (ifc_component_sync *)this;
+ }
+ return NErr_Unknown;
+}
+//-------------------------------------------
+int ServiceManager::GetServiceIndex(GUID key)
+{
+ for(int idx = 0; idx < services_indexer.size(); idx++)
+ {
+ if (memcmp(&key, &services_indexer[idx], sizeof(GUID)) == 0)
+ {
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+int ServiceManager::Service_Register(ifc_serviceFactory *svc)
+{
+ AutoLock lock(serviceGuard LOCKNAME("ServiceManager::service_register"));
+ GUID service_type = svc->GetServiceType();
+ GUID service_id = svc->GetGUID();
+
+ // add the service to the master list
+ ifc_serviceFactory* new_factory = services[service_id];
+ if (new_factory) // if someone already has this GUID, we need to replace
+ {
+ // replace factory in services_by_type map
+ ServiceList* type_list = services_by_type[service_type];
+
+ if (type_list)
+ {
+ for (ServiceList::iterator itr=type_list->begin();itr!=type_list->end();itr++)
+ {
+ ifc_serviceFactory *f = *itr;
+ if (f->GetGUID() == service_id)
+ {
+ *itr = svc;
+ }
+ }
+ }
+ // tell the old factory we're kicking its ass to the curb.
+ new_factory->ServiceNotify(ifc_serviceFactory::ONUNREGISTERED);
+ // HAKAN:
+ // Should we delete old factory?
+ // new_factory = svc;
+ }
+ else // not used yet, just assign
+ {
+ //new_factory = svc;
+ services_indexer.push_back(service_id);
+
+ // add it to the by-type lookup
+ ServiceList *&type_list = services_by_type[service_type];
+ if (!type_list)
+ type_list = new ServiceList;
+
+ type_list->push_back(svc);
+ }
+
+ services[service_id]=svc;
+
+ // send notifications
+ svc->ServiceNotify(ifc_serviceFactory::ONREGISTERED);
+
+ WASABI2_API_SYSCB->IssueCallback(Service::event_type,
+ Service::on_register,
+ (intptr_t)&service_type, reinterpret_cast<intptr_t>(svc));
+ return NErr_Success;
+}
+
+int ServiceManager::Service_Unregister(ifc_serviceFactory *svc)
+{
+ AutoLock lock(serviceGuard LOCKNAME("ServiceManager::Service_Unregister"));
+ GUID service_type = svc->GetServiceType();
+ GUID service_id = svc->GetGUID();
+
+ // remove it from the master list
+ ServiceMap::iterator itr = services.find(service_id);
+ if (itr != services.end())
+ services.erase(itr);
+
+ // and from the type lookup map
+ ServiceList *type_list = services_by_type[service_type];
+ if (type_list)
+ {
+ //type_list->eraseObject(svc);
+ for (auto it = type_list->begin(); it != type_list->end(); it++)
+ {
+ if (*it == svc)
+ {
+ it = type_list->erase(it);
+ break;
+ }
+ }
+ }
+ WASABI2_API_SYSCB->IssueCallback(Service::event_type, Service::on_deregister, (intptr_t)&service_type, reinterpret_cast<intptr_t>(svc));
+ svc->ServiceNotify(ifc_serviceFactory::ONUNREGISTERED);
+
+ return NErr_Success;
+}
+
+size_t ServiceManager::Service_GetServiceCount(GUID svc_type)
+{
+ AutoLock lock(serviceGuard LOCKNAME("ServiceManager::Service_GetServiceCount"));
+ ServiceList *type_list = services_by_type[svc_type];
+ if (type_list)
+ return type_list->size();
+ else
+ return 0;
+}
+
+ifc_serviceFactory *ServiceManager::Service_EnumService(GUID svc_type, size_t n)
+{
+ AutoLock lock(serviceGuard LOCKNAME("ServiceManager::Service_EnumService"));
+ ServiceList *type_list = services_by_type[svc_type];
+ if (type_list && (size_t)n < type_list->size())
+ return type_list->at(n);
+ else
+ return 0;
+}
+
+ifc_serviceFactory *ServiceManager::Service_EnumService(size_t n)
+{
+ AutoLock lock(serviceGuard LOCKNAME("ServiceManager::Service_EnumService"));
+ if ((size_t)n < services.size())
+ {
+ //return services.at(n).second;
+ if (n < services_indexer.size())
+ {
+ GUID g = services_indexer[n];
+ return services[g];
+ }
+ }
+
+ return 0;
+}
+
+ifc_serviceFactory *ServiceManager::Service_GetServiceByGUID(GUID guid)
+{
+ AutoLock lock(serviceGuard LOCKNAME("ServiceManager::service_getServiceByGuid"));
+ ServiceMap::iterator itr = services.find(guid);
+ if (itr != services.end())
+ return itr->second;
+ else
+ return 0;
+}
+
+void ServiceManager::Service_ComponentDone()
+{
+#ifdef _WIN32
+ ReleaseSemaphore(component_wait, 1, NULL);
+#else
+ sem_post(&component_wait);
+#endif
+}
+
+int ServiceManager::ComponentSync_Wait(size_t count)
+{
+ while (count--)
+ {
+#ifdef _WIN32
+ WaitForSingleObject(component_wait, INFINITE);
+#else
+ sem_wait(&component_wait);
+#endif
+ }
+ return NErr_Success;
+}
diff --git a/Src/replicant/Wasabi/ServiceManager.h b/Src/replicant/Wasabi/ServiceManager.h
new file mode 100644
index 000000000..efcf70dfd
--- /dev/null
+++ b/Src/replicant/Wasabi/ServiceManager.h
@@ -0,0 +1,52 @@
+#pragma once
+#include "foundation/guid.h"
+#include "service/api_service.h"
+#include "nu/AutoLock.h"
+#include "foundation/guid.h"
+#include <vector>
+#include <map>
+#include "service/ifc_servicefactory.h"
+#include "component/ifc_component_sync.h"
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <semaphore.h>
+#endif
+
+class ServiceManager : public api_service, public ifc_component_sync
+{
+public:
+ ServiceManager();
+ ~ServiceManager();
+ int WASABICALL Dispatchable_QueryInterface(GUID interface_guid, void **object);
+
+ int WASABICALL Service_Register(ifc_serviceFactory *svc);
+ int WASABICALL Service_Unregister(ifc_serviceFactory *svc);
+ size_t WASABICALL Service_GetServiceCount(GUID svc_type);
+ ifc_serviceFactory *WASABICALL Service_EnumService(GUID svc_type, size_t n);
+ ifc_serviceFactory *WASABICALL Service_EnumService(size_t n);
+ ifc_serviceFactory *WASABICALL Service_GetServiceByGUID(GUID guid);
+ void WASABICALL Service_ComponentDone();
+
+private:
+ int WASABICALL ComponentSync_Wait(size_t count);
+ int GetServiceIndex(GUID key);
+
+private:
+ nu::LockGuard serviceGuard;
+ typedef std::map<GUID, ifc_serviceFactory*> ServiceMap;
+ ServiceMap services;
+ std::vector<GUID> services_indexer;
+
+ typedef std::vector<ifc_serviceFactory*> ServiceList;
+ std::map<GUID, ServiceList*> services_by_type;
+
+
+#ifdef _WIN32
+ HANDLE component_wait;
+#else
+ sem_t component_wait;
+#endif
+};
+
+extern ServiceManager service_manager;
diff --git a/Src/replicant/Wasabi/SysCallbacks.cpp b/Src/replicant/Wasabi/SysCallbacks.cpp
new file mode 100644
index 000000000..c77eaafd4
--- /dev/null
+++ b/Src/replicant/Wasabi/SysCallbacks.cpp
@@ -0,0 +1,98 @@
+#include "SysCallbacks.h"
+#include "syscb/ifc_syscallback.h"
+
+using namespace nu;
+
+SysCallbacks::SysCallbacks()
+{
+ reentry=0;
+ inCallback=false;
+}
+
+//note: it's OK to add in the middle of an issueCallback
+//because new callbacks go at the end of the list
+//and the lockguard prevents list corruption
+int SysCallbacks::SysCallbacks_RegisterCallback(ifc_sysCallback *cb)
+{
+ AutoLock lock(callbackGuard LOCKNAME("SysCallbacks::syscb_registerCallback"));
+ callbacks.push_back(cb);
+ return 0;
+}
+
+int SysCallbacks::SysCallbacks_UnregisterCallback(ifc_sysCallback *cb)
+{
+ AutoLock lock(callbackGuard LOCKNAME("SysCallbacks::syscb_deregisterCallback"));
+ if (inCallback)
+ deleteMeAfterCallbacks.push_back(cb);
+ else
+ {
+ //callbacks.eraseAll(cb);
+ auto it = callbacks.begin();
+ while (it != callbacks.end())
+ {
+ if (*it != cb)
+ {
+ it++;
+ continue;
+ }
+
+ it = callbacks.erase(it);
+ }
+ }
+ return 0;
+}
+
+int SysCallbacks::SysCallbacks_IssueCallback( GUID eventtype, int msg, intptr_t param1, intptr_t param2 )
+{
+ AutoLock lock( callbackGuard LOCKNAME( "SysCallbacks::syscb_issueCallback" ) );
+ reentry++;
+ inCallback = true;
+
+ for ( ifc_sysCallback *l_call_back : callbacks )
+ {
+ //if (!deleteMeAfterCallbacks.contains(callbacks[i]) && callbacks[i]->GetEventType() == eventtype)
+ if ( deleteMeAfterCallbacks.end() == std::find( deleteMeAfterCallbacks.begin(), deleteMeAfterCallbacks.end(), l_call_back ) && l_call_back->GetEventType() == eventtype )
+ l_call_back->Notify( msg, param1, param2 );
+ }
+ inCallback = false;
+ reentry--;
+ if ( reentry == 0 )
+ {
+ for ( ifc_sysCallback *l_delete_me_after_call_back : deleteMeAfterCallbacks )
+ {
+ //callbacks.eraseAll(deleteMeAfterCallbacks[i]);
+ auto it = callbacks.begin();
+ while ( it != callbacks.end() )
+ {
+ if ( *it != l_delete_me_after_call_back )
+ {
+ it++;
+ continue;
+ }
+
+ it = callbacks.erase( it );
+ }
+ }
+ deleteMeAfterCallbacks.clear();
+ }
+ return 0;
+}
+
+ifc_sysCallback *SysCallbacks::SysCallbacks_Enum( GUID eventtype, size_t n )
+{
+ AutoLock lock( callbackGuard LOCKNAME( "SysCallbacks::syscb_enum" ) );
+ // TODO: maybe check !deleteMeAfterCallbacks.contains(callbacks[i])
+ for ( ifc_sysCallback *callback : callbacks )
+ {
+ if ( callback->GetEventType() == eventtype )
+ {
+ if ( n-- == 0 )
+ {
+ // benski> don't be fooled. most objects don't actually support reference counting
+ callback->Retain();
+ return callback;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/Src/replicant/Wasabi/SysCallbacks.h b/Src/replicant/Wasabi/SysCallbacks.h
new file mode 100644
index 000000000..e570b2bea
--- /dev/null
+++ b/Src/replicant/Wasabi/SysCallbacks.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "syscb/api_syscb.h"
+#include <vector>
+#include "nu/AutoLock.h"
+#include "service/types.h"
+#include "nx/nxstring.h"
+#include "nswasabi/ServiceName.h"
+
+class SysCallbacks : public api_syscb
+{
+public:
+ WASABI_SERVICE_NAME("System Callbacks API");
+
+public:
+ SysCallbacks();
+ int WASABICALL SysCallbacks_RegisterCallback(ifc_sysCallback *cb);
+ int WASABICALL SysCallbacks_UnregisterCallback(ifc_sysCallback *cb);
+ int WASABICALL SysCallbacks_IssueCallback(GUID eventtype, int msg, intptr_t param1 = 0, intptr_t param2 = 0);
+ ifc_sysCallback *WASABICALL SysCallbacks_Enum(GUID eventtype, size_t n);
+
+private:
+ nu::LockGuard callbackGuard;
+ std::vector<ifc_sysCallback*> callbacks;
+ std::vector<ifc_sysCallback*> deleteMeAfterCallbacks;
+ bool inCallback;
+ volatile int reentry;
+};
+
+extern SysCallbacks system_callbacks;
+
diff --git a/Src/replicant/Wasabi/VERSION b/Src/replicant/Wasabi/VERSION
new file mode 100644
index 000000000..ea710abb9
--- /dev/null
+++ b/Src/replicant/Wasabi/VERSION
@@ -0,0 +1 @@
+1.2 \ No newline at end of file
diff --git a/Src/replicant/Wasabi/Wasabi-replicant.sln b/Src/replicant/Wasabi/Wasabi-replicant.sln
new file mode 100644
index 000000000..5e2343fb0
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi-replicant.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Wasabi-replicant", "Wasabi-replicant.vcxproj", "{4F34CA12-9F74-4A96-A917-8DEEA4961B31}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|Win32 = Debug|Win32
+ Release|x64 = Release|x64
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Debug|x64.ActiveCfg = Debug|x64
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Debug|x64.Build.0 = Debug|x64
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Debug|Win32.Build.0 = Debug|Win32
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Release|x64.ActiveCfg = Release|x64
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Release|x64.Build.0 = Release|x64
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Release|Win32.ActiveCfg = Release|Win32
+ {4F34CA12-9F74-4A96-A917-8DEEA4961B31}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E4791259-2DCD-4881-9B00-FB79EE40D483}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/Wasabi/Wasabi-replicant.vcxproj b/Src/replicant/Wasabi/Wasabi-replicant.vcxproj
new file mode 100644
index 000000000..90d69db53
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi-replicant.vcxproj
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{4F34CA12-9F74-4A96-A917-8DEEA4961B31}</ProjectGuid>
+ <RootNamespace>Wasabi</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="api.cpp" />
+ <ClCompile Include="ServiceManager.cpp" />
+ <ClCompile Include="SysCallbacks.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__wasabi-replicant.h" />
+ <ClInclude Include="precomp.h" />
+ <ClInclude Include="ServiceManager.h" />
+ <ClInclude Include="SysCallbacks.h" />
+ <ClInclude Include="Wasabi.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/Wasabi/Wasabi-replicant.vcxproj.filters b/Src/replicant/Wasabi/Wasabi-replicant.vcxproj.filters
new file mode 100644
index 000000000..178a92a69
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi-replicant.vcxproj.filters
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="api.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ServiceManager.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SysCallbacks.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__wasabi-replicant.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="precomp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ServiceManager.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SysCallbacks.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Wasabi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{95e2a764-deca-4145-a564-704813803543}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{7214e4d1-7da7-46b4-961e-a698574d8f9c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{966f3869-2bed-42a3-885b-6104297b3673}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/Wasabi/Wasabi.h b/Src/replicant/Wasabi/Wasabi.h
new file mode 100644
index 000000000..90f800802
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi.h
@@ -0,0 +1,10 @@
+#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int Wasabi_Init();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/Wasabi/Wasabi.vcxproj b/Src/replicant/Wasabi/Wasabi.vcxproj
new file mode 100644
index 000000000..75404ff29
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi.vcxproj
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="api.cpp" />
+ <ClCompile Include="ServiceManager.cpp" />
+ <ClCompile Include="SysCallbacks.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h" />
+ <ClInclude Include="ServiceManager.h" />
+ <ClInclude Include="SysCallbacks.h" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{ABB5A3D1-9932-46B1-9488-F147C8B97868}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>Wasabi</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/Wasabi/Wasabi.xcodeproj/project.pbxproj b/Src/replicant/Wasabi/Wasabi.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..a15257822
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi.xcodeproj/project.pbxproj
@@ -0,0 +1,457 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 00B733F3151BAC6B00A8251C /* Wasabi-prepare */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 00B733F4151BAC6B00A8251C /* Build configuration list for PBXAggregateTarget "Wasabi-prepare" */;
+ buildPhases = (
+ 00B733FA151BACD800A8251C /* Generate Version Info */,
+ );
+ dependencies = (
+ 0039B365152A1EF600D96D3E /* PBXTargetDependency */,
+ );
+ name = "Wasabi-prepare";
+ productName = "Wasabi-version";
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 008840BF1506528900625F51 /* Wasabi.h in Headers */ = {isa = PBXBuildFile; fileRef = B195370314F5F8390056BB8C /* Wasabi.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B733A0151B6E2200A8251C /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B7339F151B6E2200A8251C /* version.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00C27EA515375730008D95CD /* precomp.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C27EA41537572F008D95CD /* precomp.h */; };
+ B152EBB114F53F24005076BA /* api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B152EBAB14F53F24005076BA /* api.cpp */; };
+ B152EBB214F53F24005076BA /* api.h in Headers */ = {isa = PBXBuildFile; fileRef = B152EBAC14F53F24005076BA /* api.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B152EBB314F53F24005076BA /* ServiceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B152EBAD14F53F24005076BA /* ServiceManager.cpp */; };
+ B152EBB414F53F24005076BA /* ServiceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = B152EBAE14F53F24005076BA /* ServiceManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B152EBB514F53F24005076BA /* SysCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B152EBAF14F53F24005076BA /* SysCallbacks.cpp */; };
+ B152EBB614F53F24005076BA /* SysCallbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = B152EBB014F53F24005076BA /* SysCallbacks.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 0039B364152A1EF600D96D3E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B152EB9B14F53EFD005076BA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00B733AD151B76EB00A8251C;
+ remoteInfo = "Wasabi-cleanup";
+ };
+ 00B733B1151B775400A8251C /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B152EB9B14F53EFD005076BA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00B733AD151B76EB00A8251C;
+ remoteInfo = "Wasabi-cleanup";
+ };
+ 00B733F7151BAC8600A8251C /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B152EB9B14F53EFD005076BA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00B733F3151BAC6B00A8251C;
+ remoteInfo = "Wasabi-version";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 00B7339E151B6D9000A8251C /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
+ 00B7339F151B6E2200A8251C /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = version.h; path = $PROJECT_DERIVED_FILE_DIR/version.h; sourceTree = "<absolute>"; };
+ 00C27EA41537572F008D95CD /* precomp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = precomp.h; sourceTree = "<group>"; };
+ B152EBA414F53EFD005076BA /* libWasabi.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libWasabi.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ B152EBAB14F53F24005076BA /* api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = api.cpp; sourceTree = "<group>"; };
+ B152EBAC14F53F24005076BA /* api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = api.h; sourceTree = "<group>"; };
+ B152EBAD14F53F24005076BA /* ServiceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ServiceManager.cpp; sourceTree = "<group>"; };
+ B152EBAE14F53F24005076BA /* ServiceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServiceManager.h; sourceTree = "<group>"; };
+ B152EBAF14F53F24005076BA /* SysCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SysCallbacks.cpp; sourceTree = "<group>"; };
+ B152EBB014F53F24005076BA /* SysCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SysCallbacks.h; sourceTree = "<group>"; };
+ B195370314F5F8390056BB8C /* Wasabi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Wasabi.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ B152EBA114F53EFD005076BA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 00B7339D151B6D7E00A8251C /* Version */ = {
+ isa = PBXGroup;
+ children = (
+ 00B7339E151B6D9000A8251C /* VERSION */,
+ 00B7339F151B6E2200A8251C /* version.h */,
+ );
+ name = Version;
+ sourceTree = "<group>";
+ };
+ B152EB9914F53EFD005076BA = {
+ isa = PBXGroup;
+ children = (
+ 00C27EA41537572F008D95CD /* precomp.h */,
+ B195370314F5F8390056BB8C /* Wasabi.h */,
+ B152EBAB14F53F24005076BA /* api.cpp */,
+ B152EBAC14F53F24005076BA /* api.h */,
+ B152EBAD14F53F24005076BA /* ServiceManager.cpp */,
+ B152EBAE14F53F24005076BA /* ServiceManager.h */,
+ B152EBAF14F53F24005076BA /* SysCallbacks.cpp */,
+ B152EBB014F53F24005076BA /* SysCallbacks.h */,
+ 00B7339D151B6D7E00A8251C /* Version */,
+ B152EBA514F53EFD005076BA /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ B152EBA514F53EFD005076BA /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ B152EBA414F53EFD005076BA /* libWasabi.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ B152EBA214F53EFD005076BA /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B152EBB214F53F24005076BA /* api.h in Headers */,
+ B152EBB414F53F24005076BA /* ServiceManager.h in Headers */,
+ B152EBB614F53F24005076BA /* SysCallbacks.h in Headers */,
+ 008840BF1506528900625F51 /* Wasabi.h in Headers */,
+ 00B733A0151B6E2200A8251C /* version.h in Headers */,
+ 00C27EA515375730008D95CD /* precomp.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXLegacyTarget section */
+ 00B733AD151B76EB00A8251C /* Wasabi-cleanup */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(NSBUILD_TOOLS_BIN_DIR)/cleanbuild --xcode-mode --libraries \"$(LIBRARY_PATH)\" \"$(PUBLIC_HEADERS_DIR)\" \"$(DWARF_DSYM_PATH)\" \"$(PROJECT_DERIVED_FILE_DIR)/version.*\"";
+ buildConfigurationList = 00B733AE151B76EB00A8251C /* Build configuration list for PBXLegacyTarget "Wasabi-cleanup" */;
+ buildPhases = (
+ );
+ buildToolPath = /bin/sh;
+ buildWorkingDirectory = "";
+ dependencies = (
+ );
+ name = "Wasabi-cleanup";
+ passBuildSettingsInEnvironment = 1;
+ productName = "Wasabi-cleanup";
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ B152EBA314F53EFD005076BA /* Wasabi */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B152EBA814F53EFD005076BA /* Build configuration list for PBXNativeTarget "Wasabi" */;
+ buildPhases = (
+ B152EBA014F53EFD005076BA /* Sources */,
+ B152EBA114F53EFD005076BA /* Frameworks */,
+ B152EBA214F53EFD005076BA /* Headers */,
+ 00B733AB151B768500A8251C /* Install Public Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 00B733B2151B775400A8251C /* PBXTargetDependency */,
+ 00B733F8151BAC8600A8251C /* PBXTargetDependency */,
+ );
+ name = Wasabi;
+ productName = Wasabi;
+ productReference = B152EBA414F53EFD005076BA /* libWasabi.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ B152EB9B14F53EFD005076BA /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0500;
+ ORGANIZATIONNAME = "Nullsoft, Inc.";
+ };
+ buildConfigurationList = B152EB9E14F53EFD005076BA /* Build configuration list for PBXProject "Wasabi" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = B152EB9914F53EFD005076BA;
+ productRefGroup = B152EBA514F53EFD005076BA /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ B152EBA314F53EFD005076BA /* Wasabi */,
+ 00B733AD151B76EB00A8251C /* Wasabi-cleanup */,
+ 00B733F3151BAC6B00A8251C /* Wasabi-prepare */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00B733AB151B768500A8251C /* Install Public Headers */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ name = "Install Public Headers";
+ outputPaths = (
+ "$(DSTROOT)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "INSTALLTOOL=\"$NSBUILD_TOOLS_BIN_DIR/installtool\"\n$INSTALLTOOL --headers-only \\\n \"$SCRIPT_INPUT_FILE_0/\" \\\n \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 00B733FA151BACD800A8251C /* Generate Version Info */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 12;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/VERSION",
+ "$(NSBUILD_TOOLS_SHARE_DIR)/nvgtool/lib-version.template.h",
+ );
+ name = "Generate Version Info";
+ outputPaths = (
+ "$(PROJECT_DERIVED_FILE_DIR)/version.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "PRODUCT_VERSION=$(cat \"$SCRIPT_INPUT_FILE_0\")\n\nif [ ! -d \"$PROJECT_DERIVED_FILE_DIR\" ]; then\n mkdir -p \"$PROJECT_DERIVED_FILE_DIR\"\nfi\n\nNVGTOOL=\"$NSBUILD_TOOLS_BIN_DIR/nvgtool\"\n$NVGTOOL --product-name \"$PRODUCT_NAME\" \\\n --product-version \"$PRODUCT_VERSION\" \\\n --input-file \"$SCRIPT_INPUT_FILE_1\" \\\n --output-file \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ B152EBA014F53EFD005076BA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B152EBB114F53F24005076BA /* api.cpp in Sources */,
+ B152EBB314F53F24005076BA /* ServiceManager.cpp in Sources */,
+ B152EBB514F53F24005076BA /* SysCallbacks.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 0039B365152A1EF600D96D3E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00B733AD151B76EB00A8251C /* Wasabi-cleanup */;
+ targetProxy = 0039B364152A1EF600D96D3E /* PBXContainerItemProxy */;
+ };
+ 00B733B2151B775400A8251C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00B733AD151B76EB00A8251C /* Wasabi-cleanup */;
+ targetProxy = 00B733B1151B775400A8251C /* PBXContainerItemProxy */;
+ };
+ 00B733F8151BAC8600A8251C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00B733F3151BAC6B00A8251C /* Wasabi-prepare */;
+ targetProxy = 00B733F7151BAC8600A8251C /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 00B733AF151B76EB00A8251C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Debug;
+ };
+ 00B733B0151B76EB00A8251C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Release;
+ };
+ 00B733F5151BAC6B00A8251C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ 00B733F6151BAC6B00A8251C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+ B152EBA614F53EFD005076BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = a;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ ONLY_ACTIVE_ARCH = YES;
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Debug;
+ };
+ B152EBA714F53EFD005076BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = a;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Release;
+ };
+ B152EBA914F53EFD005076BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ B152EBAA14F53EFD005076BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 00B733AE151B76EB00A8251C /* Build configuration list for PBXLegacyTarget "Wasabi-cleanup" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00B733AF151B76EB00A8251C /* Debug */,
+ 00B733B0151B76EB00A8251C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 00B733F4151BAC6B00A8251C /* Build configuration list for PBXAggregateTarget "Wasabi-prepare" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00B733F5151BAC6B00A8251C /* Debug */,
+ 00B733F6151BAC6B00A8251C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B152EB9E14F53EFD005076BA /* Build configuration list for PBXProject "Wasabi" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B152EBA614F53EFD005076BA /* Debug */,
+ B152EBA714F53EFD005076BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B152EBA814F53EFD005076BA /* Build configuration list for PBXNativeTarget "Wasabi" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B152EBA914F53EFD005076BA /* Debug */,
+ B152EBAA14F53EFD005076BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = B152EB9B14F53EFD005076BA /* Project object */;
+}
diff --git a/Src/replicant/Wasabi/Wasabi.xcodeproj/xcshareddata/xcschemes/Wasabi.xcscheme b/Src/replicant/Wasabi/Wasabi.xcodeproj/xcshareddata/xcschemes/Wasabi.xcscheme
new file mode 100644
index 000000000..1a3562793
--- /dev/null
+++ b/Src/replicant/Wasabi/Wasabi.xcodeproj/xcshareddata/xcschemes/Wasabi.xcscheme
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0500"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "B152EBA314F53EFD005076BA"
+ BuildableName = "libWasabi.a"
+ BlueprintName = "Wasabi"
+ ReferencedContainer = "container:Wasabi.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Src/replicant/Wasabi/api.cpp b/Src/replicant/Wasabi/api.cpp
new file mode 100644
index 000000000..5ff52e742
--- /dev/null
+++ b/Src/replicant/Wasabi/api.cpp
@@ -0,0 +1,15 @@
+#include "Wasabi.h"
+#include "api__wasabi-replicant.h"
+#include "nswasabi/singleton.h"
+#include "foundation/error.h"
+
+SysCallbacks system_callbacks;
+ServiceManager service_manager;
+
+static SingletonServiceFactory<SysCallbacks, api_syscb> syscb_factory;
+
+int Wasabi_Init()
+{
+ syscb_factory.Register(WASABI2_API_SVC, WASABI2_API_SYSCB);
+ return NErr_Success;
+}
diff --git a/Src/replicant/Wasabi/api__wasabi-replicant.h b/Src/replicant/Wasabi/api__wasabi-replicant.h
new file mode 100644
index 000000000..b5ffd4e5c
--- /dev/null
+++ b/Src/replicant/Wasabi/api__wasabi-replicant.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "ServiceManager.h"
+#define WASABI2_API_SVC (&service_manager)
+
+#include "SysCallbacks.h"
+#define WASABI2_API_SYSCB (&system_callbacks)
diff --git a/Src/replicant/Wasabi/precomp.h b/Src/replicant/Wasabi/precomp.h
new file mode 100644
index 000000000..be6449c01
--- /dev/null
+++ b/Src/replicant/Wasabi/precomp.h
@@ -0,0 +1,11 @@
+//
+// precomp.h
+// Wasabi
+//
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#ifdef __cplusplus
+#include "api__wasabi-replicant.h"
+#endif
diff --git a/Src/replicant/application/api_application.h b/Src/replicant/application/api_application.h
new file mode 100644
index 000000000..cf3536100
--- /dev/null
+++ b/Src/replicant/application/api_application.h
@@ -0,0 +1,62 @@
+#pragma once
+#include "../replicant/foundation/dispatch.h"
+#include "../replicant/foundation/error.h"
+#include "../replicant/service/types.h"
+#include "../replicant/nx/nxuri.h"
+
+// {23B96771-09D7-46d3-9AE2-20DCEA6C86EA}
+static const GUID applicationApiServiceGuid =
+{
+ 0x23b96771, 0x9d7, 0x46d3, { 0x9a, 0xe2, 0x20, 0xdc, 0xea, 0x6c, 0x86, 0xea }
+};
+
+// ----------------------------------------------------------------------------
+class api_application: public Wasabi2::Dispatchable
+{
+protected:
+ api_application() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_application() {}
+public:
+ static GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+ static GUID GetServiceGUID() { return applicationApiServiceGuid; }
+ const char *GetUserAgent() { return Application_GetUserAgent(); }
+
+ /* returns a path where you can store data files, if you need to */
+ int GetDataPath(nx_uri_t *path) { return Application_GetDataPath(path); }
+
+ /* checks whether or not a particular feature has permissions to operate.
+ returns NErr_True or NErr_False. see features.h for known GUIDs */
+ int GetPermission(GUID feature) { return Application_GetPermission(feature); }
+
+ /* checks whether or not a particular feature is available.
+ This only includes some features that might be absent based on OS version, hardware support, or third party dependencies
+ It's meant for code that is otherwise unable to easily check directly or via other methods (e.g. WASABI2_API_SVC->GetService)
+ returns NErr_True or NErr_False. see features.h for known GUIDs */
+ int GetFeature(GUID feature) { return Application_GetFeature(feature); }
+
+ /* used by a component to set features that are available. See notes above for GetFeature
+ for thread-safety, you should only call this during your RegisterServices() function
+ (or during application init if you are hosting Wasabi) */
+ void SetFeature(GUID feature) { Application_SetFeature(feature); }
+
+ unsigned int GetBuildNumber() { return Application_GetBuildNumber(); }
+
+ int GetVersionString(nx_string_t *version) { return Application_GetVersionString(version); }
+ int GetProductShortName(nx_string_t *name) { return Application_GetProductShortName(name); }
+ int GetDeviceID(nx_string_t *value) { return Application_GetDeviceID(value); }
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual const char * Application_GetUserAgent()=0;
+ virtual int Application_GetDataPath(nx_uri_t *path)=0;
+ virtual int Application_GetPermission(GUID feature)=0;
+ virtual int Application_GetFeature(GUID feature)=0;
+ virtual void Application_SetFeature(GUID feature)=0;
+ virtual unsigned int Application_GetBuildNumber() { return 0; }
+ virtual int Application_GetVersionString(nx_string_t *version)=0;
+ virtual int Application_GetProductShortName(nx_string_t *name)=0;
+ virtual int Application_GetDeviceID(nx_string_t *value)=0;
+};
+
diff --git a/Src/replicant/application/features.h b/Src/replicant/application/features.h
new file mode 100644
index 000000000..33473b979
--- /dev/null
+++ b/Src/replicant/application/features.h
@@ -0,0 +1,92 @@
+#pragma once
+#include "foundation/types.h"
+#include "foundation/mkncc.h"
+#ifdef __cplusplus
+#include "syscb/ifc_syscallback.h"
+#endif
+/*
+This is a listing of features that can be added to Application::SetPermission and api_application::SetFeature
+or queried with api_application::GetPermission or api_application::GetFeature
+
+if you are linking from a C file, prepend application_feature_ before each of these guids
+if you are linking from a C++ file, these are in the Features namespace
+*/
+#ifdef __cplusplus
+extern "C" {
+#define FEATURE(x) x
+#define PERMISSION(x) x
+ namespace Features {
+#else
+#define FEATURE(x) application_feature_ ## x
+#define PERMISSION(x) x application_feature_ ## x
+#endif
+
+/* these gets sent via api_sysb when permissions change */
+
+// {33C86C8A-281E-4CA7-88B0-9AF9A06C486D}
+static const GUID FEATURE(event_type) =
+{ 0x33c86c8a, 0x281e, 0x4ca7, { 0x88, 0xb0, 0x9a, 0xf9, 0xa0, 0x6c, 0x48, 0x6d } };
+
+static const int FEATURE(permissions_changed) = 0;
+static const int FEATURE(features_changed) = 1;
+
+#ifdef __cplusplus
+class SystemCallback : public ifc_sysCallback
+{
+protected:
+ GUID WASABICALL SysCallback_GetEventType() { return event_type; }
+ int WASABICALL SysCallback_Notify(int msg, intptr_t param1, intptr_t param2)
+ {
+ switch(msg)
+ {
+ case permissions_changed:
+ return FeaturesSystemCallback_OnPermissionsChanged();
+ case features_changed:
+ return FeaturesSystemCallback_OnFeaturesChanged();
+ default:
+ return NErr_Success;
+ }
+ }
+ virtual int WASABICALL FeaturesSystemCallback_OnPermissionsChanged() { return NErr_Success; }
+ virtual int WASABICALL FeaturesSystemCallback_OnFeaturesChanged() { return NErr_Success; }
+};
+#endif
+
+#if 0
+// {2E9CE2F8-E26D-4629-A3FF-5DF619136B2C}
+static const GUID PERMISSION(crossfade) =
+{ 0x2e9ce2f8, 0xe26d, 0x4629, { 0xa3, 0xff, 0x5d, 0xf6, 0x19, 0x13, 0x6b, 0x2c } };
+
+// {F7CCB4E1-CD8B-4D0A-973A-0C1F15FDDAE2}
+static const GUID PERMISSION(replaygain) =
+{ 0xf7ccb4e1, 0xcd8b, 0x4d0a, { 0x97, 0x3a, 0xc, 0x1f, 0x15, 0xfd, 0xda, 0xe2 } };
+
+// {9A52404E-764D-416D-9EC9-33AA84FBA13F}
+static const GUID PERMISSION(equalizer) =
+{ 0x9a52404e, 0x764d, 0x416d, { 0x9e, 0xc9, 0x33, 0xaa, 0x84, 0xfb, 0xa1, 0x3f } };
+#endif
+// {69663D62-5EC5-4B25-B91C-65E388C85E09}
+static const GUID FEATURE(aac_playback) =
+{ 0x69663d62, 0x5ec5, 0x4b25, { 0xb9, 0x1c, 0x65, 0xe3, 0x88, 0xc8, 0x5e, 0x9 } };
+
+// {281E7D5B-3A46-4F60-9026-D3FCBFCDD2BB}
+static const GUID PERMISSION(gapless) =
+{ 0x281e7d5b, 0x3a46, 0x4f60, { 0x90, 0x26, 0xd3, 0xfc, 0xbf, 0xcd, 0xd2, 0xbb } };
+
+// {4D6A0C67-D2FF-4F81-BA4F-369AF90BE680}
+static const GUID PERMISSION(flac_playback) =
+{ 0x4d6a0c67, 0xd2ff, 0x4f81, { 0xba, 0x4f, 0x36, 0x9a, 0xf9, 0xb, 0xe6, 0x80 } };
+
+// {5599E564-400B-40EC-8393-A58D4A6BEBD9}
+static const GUID PERMISSION(gracenote_autotag) =
+{ 0x5599e564, 0x400b, 0x40ec, { 0x83, 0x93, 0xa5, 0x8d, 0x4a, 0x6b, 0xeb, 0xd9 } };
+
+// {1A9106D7-C226-4B72-9A30-E4977A519234}
+static const GUID PERMISSION(alac_playback) =
+{ 0x1a9106d7, 0xc226, 0x4b72, { 0x9a, 0x30, 0xe4, 0x97, 0x7a, 0x51, 0x92, 0x34 } };
+
+
+#ifdef __cplusplus
+}
+}
+#endif
diff --git a/Src/replicant/audio/ifc_audio_decoder_callback.h b/Src/replicant/audio/ifc_audio_decoder_callback.h
new file mode 100644
index 000000000..2db3fa1dd
--- /dev/null
+++ b/Src/replicant/audio/ifc_audio_decoder_callback.h
@@ -0,0 +1,57 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+
+/* this is the class you actually use */
+class ifc_audio_decoder_callback : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audio_decoder_callback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audio_decoder_callback() {}
+public:
+ /* you must implement this class to use the decoder */
+ class callback : public Wasabi2::Dispatchable
+ {
+ protected:
+ callback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~callback() {}
+ public:
+ /* frames is defined as all channels, e.g. 16bit stereo is 4 bytes per frame (2 bytes per sample)
+ return NErr_Success to continue receiving callbacks
+ */
+ int OnAudio(const void *buffer, size_t buffer_frames) { return AudioDecoderCallback_OnAudio(buffer, buffer_frames); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+ private:
+ virtual int WASABICALL AudioDecoderCallback_OnAudio(const void *buffer, size_t buffer_frames)=0;
+ };
+
+ /* if possible, returns an upper bound on the number of frames used internally. this would be the maximum buffer_frames value you receive in a callback */
+ int GetFrameSize(size_t *frame_size) { return AudioDecoderCallback_GetFrameSize(frame_size); }
+ int GetMetadata(ifc_metadata **metadata) { return AudioDecoderCallback_GetMetadata(metadata); }
+
+ /* returns
+ * NErr_Success on a successfully completed decode
+ * NErr_Interrupted if the callback function aborted decoding
+ * anything else indicates a decoding error
+ */
+ int Decode(ifc_audio_decoder_callback::callback *callback) { return AudioDecoderCallback_Decode(callback); }
+
+ /* Like decode, but only processes one frame.
+ returns NErr_EndOfFile on the last frame */
+ int DecodeStep(ifc_audio_decoder_callback::callback *callback) { return AudioDecoderCallback_DecodeStep(callback); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL AudioDecoderCallback_Decode(ifc_audio_decoder_callback::callback *callback)=0;
+ virtual int WASABICALL AudioDecoderCallback_DecodeStep(ifc_audio_decoder_callback::callback *callback)=0;
+ virtual int WASABICALL AudioDecoderCallback_GetFrameSize(size_t *frame_size)=0;
+ virtual int WASABICALL AudioDecoderCallback_GetMetadata(ifc_metadata **metadata)=0;
+};
diff --git a/Src/replicant/audio/ifc_audio_decoder_packet.h b/Src/replicant/audio/ifc_audio_decoder_packet.h
new file mode 100644
index 000000000..cd9298993
--- /dev/null
+++ b/Src/replicant/audio/ifc_audio_decoder_packet.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+
+class ifc_audio_decoder_packet : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audio_decoder_packet() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audio_decoder_packet() {}
+public:
+
+ int GetMetadata(ifc_metadata **metadata) { return AudioDecoderPacket_GetMetadata(metadata); }
+ int Decode(void **out_packet, size_t *frames_available) { return AudioDecoderPacket_Decode(out_packet, frames_available); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL AudioDecoderPacket_GetMetadata(ifc_metadata **metadata)=0;
+ virtual int WASABICALL AudioDecoderPacket_Decode(void **out_packet, size_t *frames_available)=0;
+};
diff --git a/Src/replicant/audio/ifc_audio_decoder_pull.h b/Src/replicant/audio/ifc_audio_decoder_pull.h
new file mode 100644
index 000000000..9be97987b
--- /dev/null
+++ b/Src/replicant/audio/ifc_audio_decoder_pull.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+
+class ifc_audio_decoder_pull : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audio_decoder_pull() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audio_decoder_pull() {}
+public:
+
+ /* if possible, returns an upper bound on the number of frames used internally. pull decoders are most optimal if you use this to malloc your buffer */
+ int GetFrameSize(size_t *frame_size) { return AudioDecoderPull_GetFrameSize(frame_size); }
+ int GetMetadata(ifc_metadata **metadata) { return AudioDecoderPull_GetMetadata(metadata); }
+
+ /* returns
+ * NErr_EndOfFile when decode is done (frames_written will be valid, but probably 0)
+ * NErr_Success on successful decode, but not end-of-file (frames_written will be valid)
+ * anything else indicates a decode error */
+ int Decode(void *buffer, size_t buffer_frames, size_t *frames_written) { return AudioDecoderPull_Decode(buffer, buffer_frames, frames_written); }
+ /* You need to call Close() when you are done (even if you Release) because some implementations might have ifc_metadata being the same object */
+ void Close() { AudioDecoderPull_Close(); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL AudioDecoderPull_GetFrameSize(size_t *frame_size)=0;
+ virtual int WASABICALL AudioDecoderPull_GetMetadata(ifc_metadata **metadata)=0;
+ virtual int WASABICALL AudioDecoderPull_Decode(void *buffer, size_t buffer_frames, size_t *frames_written)=0;
+ virtual void WASABICALL AudioDecoderPull_Close()=0;
+};
diff --git a/Src/replicant/audio/ifc_audioout.h b/Src/replicant/audio/ifc_audioout.h
new file mode 100644
index 000000000..1ebd9e196
--- /dev/null
+++ b/Src/replicant/audio/ifc_audioout.h
@@ -0,0 +1,76 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "audio/parameters.h"
+
+
+class NOVTABLE ifc_audioout : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_audioout() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_audioout() {}
+
+public:
+ enum
+ {
+ CHANNEL_LAYOUT_MICROSOFT = 0x0, // microsoft channel order - http://www.microsoft.com/whdc/device/audio/multichaud.mspx#E4C
+ CHANNEL_LAYOUT_MPEG = 0x1,
+ };
+
+
+ enum
+ {
+ EXTENDED_FLAG_APPLY_GAIN=0x1, /* apply the gain value specified in Parameters::gain */
+ EXTENDED_FLAG_REPLAYGAIN=0x2, /* pass if you tried to figure out ReplayGain on your own. otherwise the Audio Output object will apply the default gain */
+ EXTENDED_FLAG_GAIN_MASK=EXTENDED_FLAG_APPLY_GAIN|EXTENDED_FLAG_REPLAYGAIN, /* a mask to check whether or not the gain value is valid */
+ /* so that you can check if a flag was set that you don't understand */
+ EXTENDED_FLAG_VALID_MASK=EXTENDED_FLAG_APPLY_GAIN|EXTENDED_FLAG_REPLAYGAIN,
+ };
+
+ struct Parameters
+ {
+ size_t sizeof_parameters;
+ nsaudio::Parameters audio;
+ /* anything after this needs sizeof_parameters to be large enough
+ AND a flag set in extended_fields_flags
+ if there's no flag for the field, it's because a default value of 0 can be assumed */
+ unsigned int extended_fields_flags; // set these if you use any of the following fields. see comment above
+ double gain; // additional gain specified by client. usually used for replaygain (so it can be combined with EQ pre-amp or float/pcm conversion)
+ size_t frames_trim_start; // number of frames to trim from the start
+ size_t frames_trim_end; // number of frames to trim from the start
+ };
+
+ int Output(const void *data, size_t data_size) { return AudioOutput_Output(data, data_size); }
+ // returns number of bytes that you can write
+ size_t CanWrite() { return AudioOutput_CanWrite(); }
+ void Flush(double seconds) { AudioOutput_Flush(seconds); }
+ void Pause(int state) { AudioOutput_Pause(state); }
+
+ /* called by the input plugin when no more output will be sent */
+ void Done() { AudioOutput_Done(); }
+ /* called by the input plugin when playback was forcefully stopped */
+ void Stop() { AudioOutput_Stop(); }
+
+ /* returns the latency in seconds (how many seconds until samples you're about to write show up at the audio output */
+ double Latency() { return AudioOutput_Latency(); }
+
+ /* only valid after a call to Done(). Returns NErr_True if there is still data in the buffer, NErr_False otherwise */
+ int Playing() { return AudioOutput_Playing(); }
+
+protected:
+ virtual int WASABICALL AudioOutput_Output(const void *data, size_t data_size)=0;
+ virtual size_t WASABICALL AudioOutput_CanWrite()=0; // returns number of bytes that you can write
+ virtual void WASABICALL AudioOutput_Flush(double seconds)=0;
+ virtual void WASABICALL AudioOutput_Pause(int state)=0;
+
+ /* called by the input plugin when no more output will be sent */
+ virtual void WASABICALL AudioOutput_Done()=0;
+ /* called by the input plugin when playback was forcefully stopped */
+ virtual void WASABICALL AudioOutput_Stop()=0;
+ virtual double WASABICALL AudioOutput_Latency()=0;
+ virtual int WASABICALL AudioOutput_Playing()=0;
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+};
diff --git a/Src/replicant/audio/ifc_equalizer.h b/Src/replicant/audio/ifc_equalizer.h
new file mode 100644
index 000000000..aabe48228
--- /dev/null
+++ b/Src/replicant/audio/ifc_equalizer.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+class ifc_equalizer : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_equalizer() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_equalizer() {}
+
+public:
+
+ int SetPreamp(double dB) { return Equalizer_SetPreamp(dB); }
+ int SetBand(unsigned int band, double dB) { return Equalizer_SetBand(band, dB); }
+ int Enable() { return Equalizer_Enable(); }
+ int Disable() { return Equalizer_Disable(); }
+private:
+ virtual int WASABICALL Equalizer_SetPreamp(double dB)=0;
+ virtual int WASABICALL Equalizer_SetBand(unsigned int band, double dB)=0;
+ virtual int WASABICALL Equalizer_Enable()=0;
+ virtual int WASABICALL Equalizer_Disable()=0;
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+};
diff --git a/Src/replicant/audio/parameters.h b/Src/replicant/audio/parameters.h
new file mode 100644
index 000000000..0e3cad179
--- /dev/null
+++ b/Src/replicant/audio/parameters.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "foundation/types.h"
+#include "foundation/guid.h"
+
+
+namespace nsaudio
+{
+ enum
+ {
+ /* when this is set, the const void * passed to AudioOutput_Output is assumed to be an array of channels,
+ e.g. (format_type == nsaudio_type_float && (format_flags & FORMAT_FLAG_NONINTERLEAVED) && number_of_channels == 2)
+ means that you pass a float *[2] to AudioOutput_Output
+ */
+ FORMAT_FLAG_INTERLEAVED=0x1,
+ FORMAT_FLAG_NONINTERLEAVED=0x2,
+ FORMAT_FLAG_NATIVE_ENDIAN=0x4,
+ FORMAT_FLAG_LITTLE_ENDIAN=0x8, /* audio is SPECIFICALLY little endian (as opposed to native endian on little-endian machines) */
+ FORMAT_FLAG_BIG_ENDIAN=0x10, /* audio is SPECIFICALLY big endian (as opposed to native endian on big-endian machines) */
+ FORMAT_FLAG_SIGNED=0x20, /* e.g. 8 bit PCM is typically unsigned (0-255) */
+ FORMAT_FLAG_UNSIGNED=0x40, /* e.g. 8 bit PCM is typically unsigned (0-255) */
+
+ /* so that you can check if a flag was set that you don't understand */
+ FORMAT_FLAG_VALID_INTERLEAVE = FORMAT_FLAG_INTERLEAVED|FORMAT_FLAG_NONINTERLEAVED,
+ FORMAT_FLAG_VALID_ENDIAN = FORMAT_FLAG_NATIVE_ENDIAN|FORMAT_FLAG_LITTLE_ENDIAN|FORMAT_FLAG_BIG_ENDIAN,
+ FORMAT_FLAG_VALID_SIGNED=FORMAT_FLAG_SIGNED|FORMAT_FLAG_UNSIGNED,
+ FORMAT_FLAG_VALID_MASK=FORMAT_FLAG_VALID_INTERLEAVE|FORMAT_FLAG_VALID_ENDIAN|FORMAT_FLAG_VALID_SIGNED,
+ };
+
+ // {4B80932C-E55F-4969-91EA-772584ABEDC2}
+ static const GUID format_type_pcm =
+ { 0x4b80932c, 0xe55f, 0x4969, { 0x91, 0xea, 0x77, 0x25, 0x84, 0xab, 0xed, 0xc2 } };
+
+ // {6D47717F-A383-4CF8-BB1E-72254BE3F9DC}
+ static const GUID format_type_float =
+ { 0x6d47717f, 0xa383, 0x4cf8, { 0xbb, 0x1e, 0x72, 0x25, 0x4b, 0xe3, 0xf9, 0xdc } };
+
+ struct Parameters
+ {
+ double sample_rate;
+ GUID format_type; // PCM, floating point, SPDIF pass-thru, etc.
+ unsigned int format_flags; // endian, interleaved, signed
+ unsigned int bytes_per_sample; // e.g. 4 for 20bit in a 32bit container
+ unsigned int bits_per_sample; // number of valid bits within the sample
+ unsigned int number_of_channels;
+ unsigned int channel_layout;
+ };
+};
diff --git a/Src/replicant/audio/types.h b/Src/replicant/audio/types.h
new file mode 100644
index 000000000..62a07104f
--- /dev/null
+++ b/Src/replicant/audio/types.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "foundation/types.h"
+
+enum Agave_PositionType
+{
+ AGAVE_PLAYPOSITION_100NANOECONDS = 0,
+ AGAVE_PLAYPOSITION_MILLISECONDS = 1,
+ AGAVE_PLAYPOSITION_SECONDS = 2,
+ AGAVE_PLAYPOSITION_HMSF= 3,
+ AGAVE_PLAYPOSITION_SAMPLE_FRAMES = 4,
+ AGAVE_PLAYPOSITION_BYTES = 5,
+ AGAVE_PLAYPOSITION_PACKETS = 6,
+};
+
+struct Agave_HMSF
+{
+ uint8_t hours;
+ uint8_t minutes;
+ uint8_t seconds;
+ uint8_t frames;
+};
+
+union Agave_Position
+{
+ uint64_t nanoseconds100; // in increments of 100 nanoseconds (microsoft style)
+ uint64_t milliseconds;
+ double seconds;
+ Agave_HMSF hmsf;
+ uint64_t sample_frames;
+ uint64_t bytes;
+ uint64_t packets;
+};
+
+struct Agave_Seek
+{
+ Agave_PositionType position_type;
+ Agave_Position position;
+};
+
+static void Agave_Seek_SetBytes(Agave_Seek *seek, uint64_t bytes)
+{
+ seek->position_type=AGAVE_PLAYPOSITION_BYTES;
+ seek->position.bytes = bytes;
+}
diff --git a/Src/replicant/component/ComponentManagerBase.cpp b/Src/replicant/component/ComponentManagerBase.cpp
new file mode 100644
index 000000000..d375f088a
--- /dev/null
+++ b/Src/replicant/component/ComponentManagerBase.cpp
@@ -0,0 +1,224 @@
+#include "ComponentManagerBase.h"
+#include "foundation/error.h"
+#include "nx/nxuri.h"
+
+ComponentManagerBase::ComponentManagerBase()
+{
+ phase=PHASE_INITIALIZE;
+ service_api=0;
+ component_sync=0;
+ framework_guid = INVALID_GUID;
+ application_guid = INVALID_GUID;
+}
+
+int ComponentManagerBase::LateLoad(ifc_component *component)
+{
+ int ret;
+
+ if (phase >= PHASE_REGISTERED)
+ {
+ ret = component->RegisterServices(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ return ret;
+ }
+ }
+
+ if (phase >= PHASE_LOADING)
+ {
+ ret = component->OnLoading(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ return ret;
+ }
+ }
+
+ if (phase >= PHASE_LOADED)
+ {
+ ret = component->OnLoaded(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ return ret;
+ }
+ }
+ return NErr_Success;
+}
+
+void ComponentManagerBase::SetServiceAPI(api_service *_service_api)
+{
+ this->service_api = _service_api;
+ service_api->QueryInterface(&component_sync);
+}
+
+int ComponentManagerBase::Load()
+{
+ if (phase != PHASE_INITIALIZE)
+ return NErr_Error;
+
+ int ret;
+
+ /* RegisterServices phase */
+ for (ComponentList::iterator itr=components.begin();itr!=components.end();)
+ {
+ ifc_component *component = *itr;
+ //ComponentList::iterator next=itr;
+ //next++;
+ ret = component->RegisterServices(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ NXURIRelease(component->component_info.filename);
+ CloseComponent(component);
+ itr = components.erase(itr);
+ }
+ else
+ {
+ itr++; // = next;
+ }
+ }
+
+ phase = PHASE_REGISTERED;
+
+ /* OnLoading phase */
+ for (ComponentList::iterator itr=components.begin();itr!=components.end();)
+ {
+ ifc_component *component = *itr;
+ //ComponentList::iterator next=itr;
+ //next++;
+ ret = component->OnLoading(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ NXURIRelease(component->component_info.filename);
+ CloseComponent(component);
+ itr = components.erase(itr);
+ }
+ else
+ {
+ itr++; // = next;
+ }
+ }
+
+ phase = PHASE_LOADING;
+
+ /* OnLoaded phase */
+ for (ComponentList::iterator itr = components.begin(); itr != components.end();)
+ {
+ ifc_component* component = *itr;
+ //ComponentList::iterator next = itr;
+ //next++;
+ ret = component->OnLoaded(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ NXURIRelease(component->component_info.filename);
+ CloseComponent(component);
+ itr = components.erase(itr);
+ }
+ else
+ {
+ itr++; // = next;
+ }
+ }
+
+ phase = PHASE_LOADED;
+
+ return NErr_Success;
+}
+
+int ComponentManagerBase::AddComponent(ifc_component *component)
+{
+ int err;
+
+ if (NULL == component)
+ return NErr_BadParameter;
+
+ if (phase > PHASE_LOADED)
+ return NErr_Error;
+
+ if (component->component_info.wasabi_version != wasabi2_component_version
+ || component->component_info.nx_api_version != nx_api_version
+ || component->component_info.nx_platform_guid != nx_platform_guid)
+ {
+ return NErr_IncompatibleVersion;
+ }
+
+ if (component->component_info.framework_guid != INVALID_GUID
+ && framework_guid != component->component_info.framework_guid)
+ {
+ return NErr_IncompatibleVersion;
+ }
+
+ if (component->component_info.application_guid != INVALID_GUID
+ && application_guid != component->component_info.application_guid)
+ {
+ return NErr_IncompatibleVersion;
+ }
+
+ for (ComponentList::iterator itr = components.begin(); itr != components.end(); itr++)
+ {
+ ifc_component *registered_component = *itr;
+ if (registered_component->component_info.component_guid == component->component_info.component_guid)
+ return NErr_Error;
+ }
+
+ err = component->Initialize(service_api);
+ if (NErr_Success != err)
+ return err;
+
+ /* if the component was added late, we'll need to run some extra stages */
+ err = LateLoad(component);
+ if (NErr_Success != err)
+ return err;
+
+ components.push_back(component);
+
+ return NErr_Success;
+}
+
+void ComponentManagerBase::SetFrameworkGUID(GUID guid)
+{
+ framework_guid = guid;
+}
+
+GUID ComponentManagerBase::GetFrameworkGUID()
+{
+ return framework_guid;
+}
+
+void ComponentManagerBase::SetApplicationGUID(GUID guid)
+{
+ application_guid = guid;
+}
+
+GUID ComponentManagerBase::GetApplicationGUID()
+{
+ return application_guid;
+}
diff --git a/Src/replicant/component/ComponentManagerBase.h b/Src/replicant/component/ComponentManagerBase.h
new file mode 100644
index 000000000..9559a3dd2
--- /dev/null
+++ b/Src/replicant/component/ComponentManagerBase.h
@@ -0,0 +1,43 @@
+#pragma once
+#include "nx/nxuri.h"
+#include "service/api_service.h"
+#include "component/ifc_component.h"
+#include <deque>
+#include "component/ifc_component_sync.h"
+
+class ComponentManagerBase
+{
+public:
+ void SetServiceAPI(api_service *service_api);
+ int Load();
+
+ void SetFrameworkGUID(GUID guid);
+ GUID GetFrameworkGUID();
+ void SetApplicationGUID(GUID guid);
+ GUID GetApplicationGUID();
+
+ virtual int AddComponent(ifc_component *component);
+
+protected:
+ ComponentManagerBase();
+ int LateLoad(ifc_component *mod);
+ enum Phase
+ {
+ PHASE_INITIALIZE=0, /* components are still being added */
+ PHASE_REGISTERED=1, /* RegisterServices() has been called on all components */
+ PHASE_LOADING=2, /* OnLoading() has been called on all components */
+ PHASE_LOADED=3, /* OnLoaded() has been called on all components */
+ };
+ Phase phase;
+ typedef std::deque<ifc_component*> ComponentList;
+ ComponentList components;
+ api_service *service_api;
+ ifc_component_sync *component_sync;
+
+ GUID framework_guid;
+ GUID application_guid;
+
+private:
+ /* your implementation needs to override this. You should call FreeLibrary(component->component_info.hModule); or dlclose(component->component_info.dl_handle); or similar */
+ virtual void CloseComponent(ifc_component *component)=0;
+};
diff --git a/Src/replicant/component/ifc_component.h b/Src/replicant/component/ifc_component.h
new file mode 100644
index 000000000..370c45805
--- /dev/null
+++ b/Src/replicant/component/ifc_component.h
@@ -0,0 +1,95 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "service/api_service.h"
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h> /* for CFBundleRef */
+#endif
+
+#include "nx/nxapi.h"
+#include "nx/nxuri.h"
+
+
+/* see http://wiki.office.aol.com/wiki/Replicant/Component */
+
+/* in very rare cases, this will get incremented */
+const int wasabi2_component_version = 0;
+
+struct WasabiComponentInfo
+{
+ /* these will be filled in by the host application loading your component */
+#if defined(_WIN32)
+ HMODULE hModule;
+#elif defined(__APPLE__)
+ /* depending on whether your component is a bundle or a dylib, one of these will be filled */
+ CFBundleRef bundle;
+ void *dl_handle; // pointer returned from dlopen
+#else
+ void *dl_handle; // pointer returned from dlopen
+#endif
+ nx_uri_t filename;
+
+ /* these are filled in during ifc_component's constructor. You don't need to change these */
+ int wasabi_version; /* major breaking changes to Wasabi (ABI changes, Dispatchable definition, etc) will change this. This is used to filter out components that are expecting something completely different than what the host API provides */
+ int nx_api_version; /* major breaking changes to the NX API will change this. */
+ GUID nx_platform_guid; /* platform that this is meant to link against. right now this mainly helps avoid loading android components on 'vanilla' linux, and vice-versa */
+
+ /* these are defaulted to INVALID_GUID. You can optionally override these in your Component's constructor */
+ GUID component_guid; /* a unique identifier for your Component */
+ GUID framework_guid; /* optionally set to the framework that your component is meant to work in. Can optionally be used as a filter by the Component Manager. This avoids loading, e.g., a replicant component on some completely different Wasabi-based framework */
+ GUID application_guid; /* if your component is meant to work with a specific application, you can define it here. Can optionally be used as a filter by the Component Manager. */
+};
+
+class NOVTABLE ifc_component : public Wasabi2::Dispatchable
+ //public wa::lists::node_ptr /* this is for internal use by the Component Manager! */
+{
+protected:
+ ifc_component(GUID component_guid) : Wasabi2::Dispatchable(DISPATCHABLE_VERSION)
+ {
+ memset(&component_info, 0, sizeof(component_info)); /* memsetting the whole thing avoids a bunch of ifdef code */
+ component_info.wasabi_version = wasabi2_component_version;
+ component_info.nx_api_version = nx_api_version;
+ component_info.nx_platform_guid = nx_platform_guid;
+ component_info.component_guid = component_guid;
+ }
+ ~ifc_component() {}
+public:
+ WasabiComponentInfo component_info; /* it's unusual for an interface to have data members, but this makes thing convienent */
+
+ int Initialize(api_service *_service_manager) { return Component_Initialize(_service_manager); }
+ int RegisterServices(api_service *_service_manager) { return Component_RegisterServices(_service_manager); }
+ int OnLoading(api_service *_service_manager) { return Component_OnLoading(_service_manager); }
+ int OnLoaded(api_service *_service_manager) {return Component_OnLoaded(_service_manager); }
+
+ int OnClosing(api_service *_service_manager) { return Component_OnClosing(_service_manager); }
+ void DeregisterServices(api_service *_service_manager) { Component_DeregisterServices(_service_manager); }
+ int OnClosed(api_service *_service_manager) { return Component_OnClosed(_service_manager); }
+ int Quit(api_service *_service_manager) { return Component_Quit(_service_manager); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+private:
+ /* these 4 will get called in sequence during component load, see http://wiki.office.aol.com/wiki/Replicant/Component for details */
+ // TODO: get rid of default implementations, eventually
+ virtual int WASABICALL Component_Initialize(api_service *_service_manager) { return NErr_Success; }
+ virtual int WASABICALL Component_RegisterServices(api_service *_service_manager)=0;
+ virtual int WASABICALL Component_OnLoading(api_service *_service_manager) { return NErr_Success; }
+ virtual int WASABICALL Component_OnLoaded(api_service *_service_manager) { return NErr_Success; }
+
+ virtual int WASABICALL Component_OnClosing(api_service *_service_manager) { return NErr_Success; }
+ virtual void WASABICALL Component_DeregisterServices(api_service *_service_manager)=0;
+ virtual int WASABICALL Component_OnClosed(api_service *_service_manager) { return NErr_Success; }
+ virtual int WASABICALL Component_Quit(api_service *_service_manager) { return NErr_Success; }
+};
+
+
+extern "C" typedef ifc_component *(*GETCOMPONENT_FUNC)();
diff --git a/Src/replicant/component/ifc_component_sync.h b/Src/replicant/component/ifc_component_sync.h
new file mode 100644
index 000000000..3e0bb74cf
--- /dev/null
+++ b/Src/replicant/component/ifc_component_sync.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+// {667F8818-AECD-4017-80EE-C43E096E68C1}
+static const GUID ifc_component_sync_interface_guid =
+{ 0x667f8818, 0xaecd, 0x4017, { 0x80, 0xee, 0xc4, 0x3e, 0x9, 0x6e, 0x68, 0xc1 } };
+
+class ifc_component_sync : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_component_sync() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_component_sync() {}
+public:
+ static GUID GetInterfaceGUID() { return ifc_component_sync_interface_guid; }
+ int Wait(size_t count) { return ComponentSync_Wait(count); }
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL ComponentSync_Wait(size_t count)=0;
+};
diff --git a/Src/replicant/component/win/ComponentManager.cpp b/Src/replicant/component/win/ComponentManager.cpp
new file mode 100644
index 000000000..ff36fa38b
--- /dev/null
+++ b/Src/replicant/component/win/ComponentManager.cpp
@@ -0,0 +1,88 @@
+#include "ComponentManager.h"
+#include "foundation/error.h"
+#include "nx/nxuri.h"
+
+int ComponentManager::AddComponent(nx_uri_t filename)
+{
+ if (phase > PHASE_LOADED)
+ return NErr_Error;
+
+ HMODULE hLib = LoadLibraryW(filename->string);
+ if (hLib)
+ {
+ GETCOMPONENT_FUNC pr = (GETCOMPONENT_FUNC)GetProcAddress(hLib, "GetWasabi2Component");
+ if (pr)
+ {
+ ifc_component *component = pr();
+ if (component)
+ {
+ if (component->component_info.wasabi_version != wasabi2_component_version
+ || component->component_info.nx_api_version != nx_api_version
+ || component->component_info.nx_platform_guid != nx_platform_guid)
+ {
+ FreeLibrary(hLib);
+ return NErr_IncompatibleVersion;
+ }
+
+ component->component_info.hModule = hLib;
+ component->component_info.filename = NXURIRetain(filename);
+ int ret = component->Initialize(service_api);
+ if (ret != NErr_Success)
+ {
+ NXURIRelease(component->component_info.filename);
+ FreeLibrary(hLib);
+ return ret;
+ }
+
+ /* if the component was added late, we'll need to run some extra stages */
+ ret = LateLoad(component);
+ if (ret != NErr_Success)
+ {
+ NXURIRelease(component->component_info.filename);
+ FreeLibrary(hLib);
+ return ret;
+ }
+
+ components.push_back(component);
+ return NErr_Success;
+ }
+ }
+ return NErr_Error;
+ }
+ else
+ {
+ return NErr_FileNotFound;
+ }
+}
+
+int ComponentManager::AddDirectory(nx_uri_t directory)
+{
+ WIN32_FIND_DATAW find_data = {0};
+
+ nx_uri_t directory_mask;
+ int ret = NXURICreateFromPath(&directory_mask, L"*.w6c", directory);
+ if (ret != NErr_Success)
+ return ret;
+
+ HANDLE find_handle = FindFirstFileW(directory_mask->string, &find_data);
+ if (find_handle != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ nx_uri_t w6c_filename;
+ if (NXURICreateFromPath(&w6c_filename, find_data.cFileName, directory) == NErr_Success)
+ {
+ AddComponent(w6c_filename);
+ NXURIRelease(w6c_filename);
+ }
+ }
+ while (FindNextFileW(find_handle,&find_data));
+ FindClose(find_handle);
+ }
+ return NErr_Success;
+}
+
+void ComponentManager::CloseComponent(ifc_component *component)
+{
+ FreeLibrary(component->component_info.hModule);
+} \ No newline at end of file
diff --git a/Src/replicant/component/win/ComponentManager.h b/Src/replicant/component/win/ComponentManager.h
new file mode 100644
index 000000000..9bc45245b
--- /dev/null
+++ b/Src/replicant/component/win/ComponentManager.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "../ComponentManagerBase.h"
+
+class ComponentManager : public ComponentManagerBase
+{
+public:
+ int AddComponent(nx_uri_t filename);
+ int AddDirectory(nx_uri_t directory);
+
+private:
+ void CloseComponent(ifc_component *component);
+}; \ No newline at end of file
diff --git a/Src/replicant/decode/api_decode.h b/Src/replicant/decode/api_decode.h
new file mode 100644
index 000000000..3b5433c7c
--- /dev/null
+++ b/Src/replicant/decode/api_decode.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "audio/ifc_audio_decoder_callback.h"
+#include "audio/parameters.h"
+#include "service/types.h"
+#include "nx/nxuri.h"
+
+// {AA4404BC-69E7-4898-9296-420F774C3331}
+static const GUID decode_api_service_guid =
+{ 0xaa4404bc, 0x69e7, 0x4898, { 0x92, 0x96, 0x42, 0xf, 0x77, 0x4c, 0x33, 0x31 } };
+
+/* benski> some of this is TODO as of 25-Jan-2012
+We want to have several ways to create a decoder
+1) Simple method. Create a decoder of the desired style (callback, pull, packet). api_decode will convert between styles if necessary
+2) Constraint method: Non-zero values for nsaudio::Parameters members are treated as requirements and api_decode will convert. Flags might indicate that sample rate or channels is a "maximum" rather than a strict requirement
+3) Multiple nsaudio::Parameters values. Used if you are a little more flexible in the data format, e.g. if you can handle 16bit audio or floating point
+*/
+class api_decode : public Wasabi2::Dispatchable
+{
+protected:
+ api_decode() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_decode() {}
+public:
+ static GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+ static GUID GetServiceGUID() { return decode_api_service_guid; }
+
+ int CreateAudioDecoder_Callback(ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return DecodeAPI_CreateAudioDecoder_Callback(decoder, filename, parameters, flags); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL DecodeAPI_CreateAudioDecoder_Callback(ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags)=0;
+};
diff --git a/Src/replicant/decode/ifc_raw_media_reader.h b/Src/replicant/decode/ifc_raw_media_reader.h
new file mode 100644
index 000000000..562c08590
--- /dev/null
+++ b/Src/replicant/decode/ifc_raw_media_reader.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "foundation/dispatch.h"
+
+class ifc_raw_media_reader : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_raw_media_reader() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_raw_media_reader() {}
+public:
+ int Read(void *buffer, size_t buffer_size, size_t *bytes_read) { return RawMediaReader_Read(buffer, buffer_size, bytes_read); }
+ /* TODO: we'll probably need stuff in here like EndOfFile, determining a good buffer size, etc */
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+protected:
+ virtual int WASABICALL RawMediaReader_Read(void *buffer, size_t buffer_size, size_t *bytes_read)=0;
+};
diff --git a/Src/replicant/decode/svc_decode.h b/Src/replicant/decode/svc_decode.h
new file mode 100644
index 000000000..bdc7851b9
--- /dev/null
+++ b/Src/replicant/decode/svc_decode.h
@@ -0,0 +1,80 @@
+#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"
+
+
+/* if you return NErr_TryAgain, you will be called again with pass=1 after all other services get a chance at the file */
+
+// DA3BB978-4A85-409F-B67C-10E3E1CF73CB
+static const GUID decode_service_type_guid =
+{ 0xDA3BB978, 0x4A85, 0x409F, { 0xB6, 0x7C, 0x10, 0xE3, 0xE1, 0xCF, 0x73, 0xCB } };
+
+class svc_decode : public Wasabi2::Dispatchable
+{
+protected:
+ svc_decode() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_decode() {}
+public:
+ static GUID GetServiceType() { return decode_service_type_guid; }
+
+ /*
+ * return values for the CreateAudioDecoder family of functions
+ * * NErr_Success: Decoder successfully created
+ * * NErr_False: File type not supported by this decoder
+ * * NErr_UnsupportedInterface: File type is supported by this decoder, but not the particular style (callback, pull, packet).
+ * * Any other code is interpreted as an error and enumeration will stop!
+ */
+
+ /* Flags are defined in ifc_audio_decoder_callback */
+ int CreateAudioDecoder_Callback(ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return DecodeService_CreateAudioDecoder_Callback(decoder, filename, parameters, flags); }
+ int CreateAudioDecoder_Pull(ifc_audio_decoder_pull **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return DecodeService_CreateAudioDecoder_Pull(decoder, filename, parameters, flags); }
+
+ int CreateAudioDecoder_Callback(unsigned int pass, ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags)
+ {
+ if (dispatchable_version == 0)
+ {
+ if (pass == 0)
+ return DecodeService_CreateAudioDecoder_Callback(decoder, filename, parameters, flags);
+ else
+ return NErr_False;
+ }
+ else
+ return DecodeService_CreateAudioDecoder_Callback(pass, decoder, filename, parameters, flags);
+ }
+
+ int CreateAudioDecoder_Pull(unsigned int pass, ifc_audio_decoder_pull **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags)
+ {
+ if (dispatchable_version == 0)
+ {
+ if (pass == 0)
+ return DecodeService_CreateAudioDecoder_Pull(decoder, filename, parameters, flags);
+ else
+ return NErr_False;
+ }
+ else
+ return DecodeService_CreateAudioDecoder_Pull(pass, decoder, filename, parameters, flags);
+ }
+
+ enum
+ {
+ /* Additional flags are defined in the specific decoder interface (e.g. ifc_audio_decoder_pull.h)
+ They should start at 0x80000001 to ensure uniqueness */
+ FLAG_NO_METADATA= (1 << 0), // tells the decoder that it doesn't need to parse metadata
+ FLAG_VALIDATION = (1 << 1), // turns on codec/file-format specific extra validation checks. for most implementations this means turning on CRC checking
+ };
+
+ enum
+ {
+ DISPATCHABLE_VERSION=1,
+ };
+private:
+ /* these two no longer have to be implemented */
+ virtual int WASABICALL DecodeService_CreateAudioDecoder_Callback(ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return DecodeService_CreateAudioDecoder_Callback(0, decoder, filename, parameters, flags); }
+ virtual int WASABICALL DecodeService_CreateAudioDecoder_Pull(ifc_audio_decoder_pull **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return DecodeService_CreateAudioDecoder_Pull(0, decoder, filename, parameters, flags); }
+
+ virtual int WASABICALL DecodeService_CreateAudioDecoder_Callback(unsigned int pass, ifc_audio_decoder_callback **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return NErr_NotImplemented; }
+ virtual int WASABICALL DecodeService_CreateAudioDecoder_Pull(unsigned int pass, ifc_audio_decoder_pull **decoder, nx_uri_t filename, nsaudio::Parameters *parameters, int flags) { return NErr_NotImplemented; }
+};
diff --git a/Src/replicant/decode/svc_raw_media_reader.h b/Src/replicant/decode/svc_raw_media_reader.h
new file mode 100644
index 000000000..178b1f35d
--- /dev/null
+++ b/Src/replicant/decode/svc_raw_media_reader.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "foundation/dispatch.h"
+#include "ifc_raw_media_reader.h"
+#include "nx/nxuri.h"
+
+// {BE616DD5-5F42-4E42-88CF-CB7DCB47A3CD}
+static const GUID svc_raw_media_reader_guid =
+{ 0xbe616dd5, 0x5f42, 0x4e42, { 0x88, 0xcf, 0xcb, 0x7d, 0xcb, 0x47, 0xa3, 0xcd } };
+
+
+class svc_raw_media_reader : public Wasabi2::Dispatchable
+{
+protected:
+ svc_raw_media_reader() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_raw_media_reader() {}
+public:
+ static GUID GetServiceType() { return svc_raw_media_reader_guid; }
+ int CreateRawMediaReader(ifc_raw_media_reader **reader, nx_uri_t filename, unsigned int pass) { return RawMediaReaderService_CreateRawMediaReader(reader, filename, pass); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION = 0,
+ };
+protected:
+ virtual int WASABICALL RawMediaReaderService_CreateRawMediaReader(ifc_raw_media_reader **reader, nx_uri_t filename, unsigned int pass)=0;
+};
diff --git a/Src/replicant/file/FileDecoder.h b/Src/replicant/file/FileDecoder.h
new file mode 100644
index 000000000..2ac59fd67
--- /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 000000000..1b231c4d9
--- /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 000000000..b6b17bec1
--- /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 000000000..b90fa3594
--- /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 000000000..2cda1293d
--- /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 000000000..e61916343
--- /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 000000000..e384c88fb
--- /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 000000000..298f5938d
--- /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 000000000..8559c8cf9
--- /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 000000000..ae9b178c0
--- /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 000000000..eed7b0d29
--- /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 000000000..ebc7d7a35
--- /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 000000000..29146cc42
--- /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;
+};
+
diff --git a/Src/replicant/filelock/api_filelock.h b/Src/replicant/filelock/api_filelock.h
new file mode 100644
index 000000000..b04810d38
--- /dev/null
+++ b/Src/replicant/filelock/api_filelock.h
@@ -0,0 +1,49 @@
+#pragma once
+#include "foundation/foundation.h"
+#include "nx/nxuri.h"
+
+#include "service/types.h"
+class cb_filelock : public Wasabi2::Dispatchable
+{
+protected:
+ cb_filelock() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~cb_filelock() {}
+public:
+ int Interrupt() { return FileLockCallback_Interrupt(); }
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL FileLockCallback_Interrupt()=0;
+};
+
+// {AC2E21C6-7C66-47F6-8C99-267D6CAA1942}
+static const GUID file_lock_service_guid =
+{ 0xac2e21c6, 0x7c66, 0x47f6, { 0x8c, 0x99, 0x26, 0x7d, 0x6c, 0xaa, 0x19, 0x42 } };
+
+
+class api_filelock : public Wasabi2::Dispatchable
+{
+protected:
+ api_filelock() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_filelock() {}
+public:
+ static GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+ static GUID GetServiceGUID() { return file_lock_service_guid; }
+ int WaitForRead(nx_uri_t filename) { return FileLock_WaitForRead(filename); }
+ int WaitForReadInterruptable(nx_uri_t filename, cb_filelock *callback) { return FileLock_WaitForReadInterruptable(filename, callback); }
+ int WaitForWrite(nx_uri_t filename) { return FileLock_WaitForWrite(filename); }
+ int WaitForWriteInterruptable(nx_uri_t filename, cb_filelock *callback) { return FileLock_WaitForWriteInterruptable(filename, callback); }
+ int UnlockFile(nx_uri_t filename) { return FileLock_UnlockFile(filename); }
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL FileLock_WaitForRead(nx_uri_t filename)=0;
+ virtual int WASABICALL FileLock_WaitForReadInterruptable(nx_uri_t filename, cb_filelock *callback)=0;
+ virtual int WASABICALL FileLock_WaitForWrite(nx_uri_t filename)=0;
+ virtual int WASABICALL FileLock_WaitForWriteInterruptable(nx_uri_t filename, cb_filelock *callback)=0;
+ virtual int WASABICALL FileLock_UnlockFile(nx_uri_t filename)=0;
+};
diff --git a/Src/replicant/foundation/align.h b/Src/replicant/foundation/align.h
new file mode 100644
index 000000000..dc232fa7e
--- /dev/null
+++ b/Src/replicant/foundation/align.h
@@ -0,0 +1,9 @@
+#pragma once
+
+# if defined(__GNUC__)
+#include <stdlib.h> // for posix_memalign
+#define NALIGN(x) __attribute__((aligned(x)))
+#elif defined(_MSC_VER)
+#include <malloc.h> // for _aligned_malloc
+#define NALIGN(x) __declspec (align(x))
+#endif
diff --git a/Src/replicant/foundation/atomics.h b/Src/replicant/foundation/atomics.h
new file mode 100644
index 000000000..799822320
--- /dev/null
+++ b/Src/replicant/foundation/atomics.h
@@ -0,0 +1,20 @@
+#pragma once
+#if defined(_WIN64) && defined(_M_X64)
+#include "win-amd64/atomics.h"
+#elif defined(_WIN32) && defined(_M_IX86)
+#include "win-x86/atomics.h"
+#elif defined(__APPLE__) && defined(__amd64__)
+#include "osx-amd64/atomics.h"
+#elif defined(__APPLE__) && defined(__i386__)
+#include "osx-x86/atomics.h"
+#elif defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
+#include "android-armv7/atomics.h"
+#elif defined(__ANDROID__) && (defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__))
+#include "android-arm/atomics.h"
+#elif defined(__ANDROID__) && defined(__i386__)
+#include "android-x86/atomics.h"
+#elif defined(__linux__) && defined(__x86_64)
+#include "linux-amd64/atomics.h"
+#else
+#error Port Me!
+#endif
diff --git a/Src/replicant/foundation/dispatch.h b/Src/replicant/foundation/dispatch.h
new file mode 100644
index 000000000..81af541bd
--- /dev/null
+++ b/Src/replicant/foundation/dispatch.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "guid.h"
+
+#ifdef WIN32
+#ifndef NOVTABLE
+#define NOVTABLE __declspec(novtable)
+#endif
+#else
+#define NOVTABLE
+#endif
+
+
+#if defined(__GNUC__) && (defined(__x86_32__) || defined(__i386__))
+#define WASABICALL __attribute__((stdcall))
+#elif defined (_WIN32)
+#define WASABICALL __stdcall
+#else
+#define WASABICALL
+#endif
+
+namespace Wasabi2
+{
+ class NOVTABLE Dispatchable
+ {
+ protected:
+ Dispatchable(size_t _dispatchable_version) : dispatchable_version(_dispatchable_version) {}
+ ~Dispatchable() {}
+ public:
+#define dispatch_call(code, default_return, func) (DispatchValid(code))?(default_return):func
+#define dispatch_voidcall(code, func) if (DispatchValid(code)) func
+
+ bool DispatchValid(size_t code) const
+ {
+ return code < dispatchable_version;
+ }
+
+ size_t Retain()
+ {
+ return Dispatchable_Retain();
+ }
+
+ size_t Release()
+ {
+ return Dispatchable_Release();
+ }
+
+ int QueryInterface(GUID interface_guid, void **object)
+ {
+ return Dispatchable_QueryInterface(interface_guid, object);
+ }
+
+ template <class ifc_t>
+ int QueryInterface(ifc_t **object)
+ {
+ return Dispatchable_QueryInterface(ifc_t::GetInterfaceGUID(), (void **)object);
+ }
+
+ protected:
+ virtual size_t WASABICALL Dispatchable_Retain() { return 0; }
+ virtual size_t WASABICALL Dispatchable_Release() { return 0; }
+ virtual int WASABICALL Dispatchable_QueryInterface(GUID interface_guid, void **object) { return 1; }
+
+ size_t dispatchable_version;
+ };
+}
+
+#ifndef DECLARE_EXTERNAL_SERVICE
+#define DECLARE_EXTERNAL_SERVICE(_type, _name) extern _type *_name
+#endif
+
+#ifndef DEFINE_EXTERNAL_SERVICE
+#define DEFINE_EXTERNAL_SERVICE(_type, _name) _type *_name=0
+#endif
diff --git a/Src/replicant/foundation/error.h b/Src/replicant/foundation/error.h
new file mode 100644
index 000000000..7ae612119
--- /dev/null
+++ b/Src/replicant/foundation/error.h
@@ -0,0 +1,88 @@
+#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+enum
+{
+ NErr_Success = 0,
+ NErr_True = 0,
+ NErr_Error = 1, // generic error
+ NErr_OutOfMemory = 2,
+ NErr_FileNotFound = 3,
+ NErr_NullPointer = 4,
+ NErr_NotImplemented = 5,// I'm a lazy programmer
+ NErr_EndOfFile = 6, // also used for "end of enumeration"
+ NErr_NeedMoreData = 7, // input buffer was too small to provide useful output. Use this instead of NErr_ReadTruncated when it is expected that the caller can call the function again with more data
+ NErr_False = 8, // returned from a bool-like function to indicate "false" as opposed to "i had an error while figuring it out"
+ NErr_FailedCreate = 9, // Object could not be created
+ NErr_Closed = 10,
+ NErr_TryAgain = 11, // often used in round-robin "isMine()" loops to indicate that you'll take it if no one else wants it first. can also be used for device I/O when the device is busy
+ NErr_NoDevice = 12,
+ NErr_UnsupportedFormat = 13,
+ NErr_Unknown = 14, // NOT meant to be "some unknown error". Usually returned when some passed in enumeration or keyword was an unknown, unexpected or unsupported value
+ NErr_Insufficient = 15, // output buffer was too small
+ NErr_Empty = 16,
+ NErr_LostSynchronization = 17,
+ NErr_TimedOut = 19,
+ NErr_BadParameter = 20,
+ NErr_NoAction = 21, // Returned when no action performed, for example when initializing but something has already been initialized
+
+ // Test case related values
+ NErr_TestFailed = 18, // Result on a test failure, typically used by unit tests and other test cases.
+ NErr_TestPassed = 0, // Result on a test success, typically used by unit tests and other test cases.
+ NErr_TestError = 1, // Result on a test error, typically used by unit tests and other test cases.
+ NErr_TestNotComplete = 22, // Result on a premature stop, typically used by unit tests and other test cases. This is to protect against a scenerio where a test case is in a 'PASSED' state up to a certain point but cannot finish execution due to data missing, environmental issues, etc.
+
+ NErr_Malformed = 23, // some peice of data was malformed or had unexpected value (typically returned by parsers)
+ NErr_WrongFormat = 24, // data was understood but is indicating a different format than expected. e.g. an layer 2 header being encountered by a layer 3 parser
+ NErr_Reserved = 25, // typically returned when a parser encounters data with a reserved flag set to true
+ NErr_Changed = 26, // something changed. e.g. samplerate changed mid-stream
+ NErr_Interrupted = 27,
+ NErr_ConnectionFailed = 28, // generic "can't connect" error
+ NErr_DNS = 29, // no DNS entry for the host
+
+ /* the follow map NError codes to HTTP error codes. but they can be used for other purposes, too */
+ NErr_BadRequest = 30, // aka HTTP 400
+ NErr_Unauthorized = 31, // aka HTTP 401
+ NErr_Forbidden = 32, // aka HTTP 403
+ NErr_NotFound = 33, // aka HTTP 404, differentiated from NErr_FileNotFound
+ NErr_BadMethod = 34, // aka HTTP 405
+ NErr_NotAcceptable = 35, // aka HTTP 406
+ NErr_ProxyAuthenticationRequired = 36, // aka HTTP 407
+ NErr_RequestTimeout = 37, // aka HTTP 408
+ NErr_Conflict = 38, // aka HTTP 409
+ NErr_Gone = 39, // aka HTTP 410
+ NErr_InternalServerError = 40, // aka HTTP 500
+ NErr_ServiceUnavailable = 41, // aka HTTP 503
+
+ NErr_Exception = 42, // Underlying library returns an error or exception that wasn't understood
+ NErr_Underrun = 43, // Asynchronous thread not supplying data fast enough, buffer has insufficient data
+ NErr_NoMatchingImplementation = 44, // Returned when a function that delegates functionality to a matching component is unable to find one e.g. api_playlistmanager::Load
+ NErr_IntegerOverflow = 45,
+ NErr_IncompatibleVersion = 46, // returned e.g. when a "size" field in a passed struct was larger than expected, or when a flag was set that's not understood
+ NErr_Disabled = 47,
+ NErr_ParameterOutOfRange = 48, // Used to signify that a paramater was passed in that is out of bounds for valid values.
+ NErr_OSNotSupported = 49, // something is not supported on this OS (e.g. WASAPI audio on Windows XP)
+ NErr_UnsupportedInterface = 50, // used for some APIs (notably svc_decode). It means that you can provide the requested functionality for the provided data (e.g. filename) but don't support the requested interface
+ NErr_DirectPointer = 51,
+ NErr_ReadOnly = 52,
+ NErr_EndOfEnumeration = NErr_EndOfFile, // we'll eventually make this its own number
+ NErr_ReadTruncated = 54, // somewhat similar to NErr_NeedMoreData. Meant to be used e.g. when a file or input buffer is shorter than expected. Use this instead of NErr_NeedMoreData when the caller cannot provide more data.
+ NErr_Aborted = 55,
+ NErr_BadReturnValue = 56, // e.g. a callback function returns an unexpected value
+ NErr_MaximumDepth = 57,
+ NErr_Stopped = 58,
+ NErr_LengthRequired = 59, // aka HTTP 411
+ NErr_PreconditionFailed = 60, // aka HTTP 411
+ NErr_TooLarge = 61, // aka HTTP 413
+};
+
+typedef int NError;
+typedef int ns_error_t; // TODO: eventually make this the name of the enum
+
+#ifdef __cplusplus
+}
+#endif
+
+// be careful. only use this if your stack variables self-destruct
+#define NSERROR_RETURN_ON_FAILURE(x) { int local_ret = x; if (local_ret != NErr_Success) return local_ret; }
diff --git a/Src/replicant/foundation/export.h b/Src/replicant/foundation/export.h
new file mode 100644
index 000000000..4454075dd
--- /dev/null
+++ b/Src/replicant/foundation/export.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#ifdef _MSC_VER
+#define DLLEXPORT __declspec(dllexport)
+#elif defined(__GNUC__)
+#define DLLEXPORT __attribute__ ((visibility("default")))
+#else
+#error port me!
+#endif
+
+#ifdef _MSC_VER
+#define DLLIMPORT __declspec(dllimport)
+#elif defined(__GNUC__)
+#define DLLIMPORT
+#else
+#error port me!
+#endif
+
diff --git a/Src/replicant/foundation/foundation.h b/Src/replicant/foundation/foundation.h
new file mode 100644
index 000000000..85db7deab
--- /dev/null
+++ b/Src/replicant/foundation/foundation.h
@@ -0,0 +1,7 @@
+#pragma once
+#include "dispatch.h"
+#include "atomics.h"
+#include "error.h"
+#include "guid.h"
+#include "mkncc.h"
+#include "types.h"
diff --git a/Src/replicant/foundation/guid.h b/Src/replicant/foundation/guid.h
new file mode 100644
index 000000000..7e638c7d2
--- /dev/null
+++ b/Src/replicant/foundation/guid.h
@@ -0,0 +1,35 @@
+#ifndef WASABI2_FOUNDATION_GUID_H
+#define WASABI2_FOUNDATION_GUID_H
+#pragma once
+
+#include "types.h"
+
+#if !defined(_WIN32)
+#ifdef __cplusplus
+#if !defined(GUID_EQUALS_DEFINED) || !defined(_SYS_GUID_OPERATOR_EQ_)
+#define GUID_EQUALS_DEFINED
+#include <memory.h>
+#include <string.h>
+static __inline int operator ==(const GUID &a, const GUID &b) {
+ return !memcmp(&a, &b, sizeof(GUID));
+}
+static __inline int operator !=(const GUID &a, const GUID &b) {
+ return !!memcmp(&a, &b, sizeof(GUID));
+}
+#endif //GUID_EQUALS_DEFINED
+#endif //__cplusplus
+#else
+#include <guiddef.h>
+#endif
+
+#ifdef __cplusplus
+static __inline int operator <(const GUID &a, const GUID &b) {
+ return memcmp(&a, &b, sizeof(GUID)) < 0;
+}
+#endif
+
+
+static const GUID INVALID_GUID = { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} };
+static const GUID GENERIC_GUID = { 0xFFFFFFFF, 0xFFFF, 0xFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} };
+
+#endif
diff --git a/Src/replicant/foundation/mkncc.h b/Src/replicant/foundation/mkncc.h
new file mode 100644
index 000000000..461e3a9a4
--- /dev/null
+++ b/Src/replicant/foundation/mkncc.h
@@ -0,0 +1,12 @@
+#ifndef WASABI2_FOUNDATION_MKNCC_H
+#define WASABI2_FOUNDATION_MKNCC_H
+#pragma once
+
+// note: this is endian-incompatible with win32's MAKEFOURCC
+// otoh, it shows up nicely in a debug register ;)
+
+#define MK4CC(a, b, c, d) ( (((unsigned long)a)<<24)|(((unsigned long)b)<<16)|(((unsigned long)c)<<8)|((unsigned long)d) )
+#define MK3CC(b, c, d) ( (((unsigned long)b)<<16)|(((unsigned long)c)<<8)|((unsigned long)d) )
+#define MK2CC(c, d) ( (((unsigned long)c)<<8)|((unsigned long)d) )
+
+#endif
diff --git a/Src/replicant/foundation/types.h b/Src/replicant/foundation/types.h
new file mode 100644
index 000000000..b9788fdcd
--- /dev/null
+++ b/Src/replicant/foundation/types.h
@@ -0,0 +1,16 @@
+#pragma once
+#if defined(_WIN64) && defined(_M_X64)
+#include "win-amd64/types.h"
+#elif defined(_WIN32) && defined(_M_IX86)
+#include "win-x86/types.h"
+#elif defined(__APPLE__) && defined(__LP64__)
+#include "osx-amd64/types.h"
+#elif defined(__APPLE__) // TODO: && defined(__LP32__)
+#include "osx-x86/types.h"
+#elif defined(__ANDROID__)
+#include "android-arm/types.h"
+#elif defined(__linux__) && defined(__x86_64)
+#include "linux-amd64/types.h"
+#else
+#error port me!
+#endif
diff --git a/Src/replicant/foundation/win-amd64/atomics.h b/Src/replicant/foundation/win-amd64/atomics.h
new file mode 100644
index 000000000..97b24d918
--- /dev/null
+++ b/Src/replicant/foundation/win-amd64/atomics.h
@@ -0,0 +1,104 @@
+/*
+
+ Win64 (amd64) implementation
+
+*/
+
+#pragma once
+#include "../../foundation/types.h"
+#include <Windows.h>
+#include <intrin.h>
+
+#ifdef __cplusplus
+#define NX_ATOMIC_INLINE inline
+#else
+#define NX_ATOMIC_INLINE
+#endif
+
+NX_ATOMIC_INLINE static size_t nx_atomic_inc(volatile size_t *addr)
+{
+ return (size_t)_InterlockedIncrement64((volatile LONGLONG *)addr);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_dec(volatile size_t *addr)
+{
+ return (size_t)_InterlockedDecrement64((volatile LONGLONG *)addr);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_dec_release(volatile size_t *addr)
+{
+ return (size_t)_InterlockedDecrement64((volatile LONGLONG *)addr);
+}
+
+NX_ATOMIC_INLINE static void nx_atomic_write(size_t value, volatile size_t *addr)
+{
+ InterlockedExchange64((volatile LONG64 *)addr, value);
+}
+
+NX_ATOMIC_INLINE static void nx_atomic_write_pointer(void *value, void* volatile *addr)
+{
+ InterlockedExchangePointer(addr, value);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_add(size_t value, volatile size_t* addr)
+{
+ return (size_t)InterlockedExchangeAdd64 ((volatile LONGLONG *)addr, (LONGLONG)value);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_sub(size_t value, volatile size_t* addr)
+{
+ return (size_t)InterlockedExchangeAdd64((volatile LONGLONG *)addr, -(LONGLONG)value);
+}
+
+NX_ATOMIC_INLINE static void *nx_atomic_swap_pointer(void *value, void* volatile *addr)
+{
+ return InterlockedExchangePointer(addr, value);
+}
+
+NX_ATOMIC_INLINE static int nx_atomic_cmpxchg_pointer(void *oldvalue, void *newvalue, void* volatile *addr)
+{
+ return InterlockedCompareExchangePointer(addr, newvalue, oldvalue) == oldvalue;
+}
+/*
+NX_ATOMIC_INLINE static int nx_atomic_cmpxchg2(size_t *oldvalue, size_t *newvalue, volatile size_t *addr)
+{
+ return InterlockedCompare64Exchange128((LONG64 volatile *)addr, (LONG64)newvalue[1], (LONG64)newvalue[0], (LONG64)oldvalue[0]) == oldvalue[0];
+}
+*/
+#if 0
+NX_ATOMIC_INLINE static size_t atomic_increment(volatile size_t *val)
+{
+ return (size_t)InterlockedIncrement((volatile LONG *)val);
+}
+
+NX_ATOMIC_INLINE static size_t atomic_decrement(volatile size_t *val)
+{
+ return (size_t)InterlockedDecrement((volatile LONG *)val);
+}
+
+NX_ATOMIC_INLINE static void atomic_add(volatile size_t *val, size_t add)
+{
+ InterlockedExchangeAdd64((volatile LONGLONG *)val, (LONGLONG)add);
+}
+
+NX_ATOMIC_INLINE static void atomic_sub(volatile size_t *val, size_t sub)
+{
+ InterlockedExchangeAdd64((volatile LONGLONG *)val, -((LONGLONG)sub));
+}
+
+NX_ATOMIC_INLINE static void *atomic_exchange_pointer(void* volatile *target, void *value)
+{
+ return InterlockedExchangePointer(target, value);
+}
+
+NX_ATOMIC_INLINE static bool atomic_compare_exchange_pointer(void* volatile *destination, void *exchange, void *compare)
+{
+ return InterlockedCompareExchangePointer(destination, exchange, compare) == compare;
+}
+
+NX_ATOMIC_INLINE static void atomic_write(volatile size_t *dest, size_t src)
+{
+ InterlockedExchange64((volatile LONG64 *)dest, src);
+}
+
+#endif \ No newline at end of file
diff --git a/Src/replicant/foundation/win-amd64/types.h b/Src/replicant/foundation/win-amd64/types.h
new file mode 100644
index 000000000..2b176f0f8
--- /dev/null
+++ b/Src/replicant/foundation/win-amd64/types.h
@@ -0,0 +1,92 @@
+#pragma once
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <wchar.h>
+// first, some standard int types
+typedef unsigned int UINT;
+typedef signed int SINT;
+
+typedef unsigned char UCHAR;
+typedef signed char SCHAR;
+
+typedef unsigned long ARGB32;
+typedef unsigned long RGB32;
+
+typedef unsigned long ARGB24;
+typedef unsigned long RGB24;
+
+typedef unsigned short ARGB16;
+typedef unsigned short RGB16;
+
+typedef unsigned long FOURCC;
+
+typedef wchar_t nsxml_char_t;
+typedef wchar_t ns_char_t;
+typedef wchar_t nsfilename_char_t;
+
+typedef int socklen_t;
+
+#if defined(_WIN64) && !defined(__GNUC__)
+#include <stddef.h>
+#include <stdint.h>
+
+ // since windows doesn't have stdint.h
+ typedef unsigned __int64 uint64_t;
+ typedef unsigned __int32 uint32_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int8 uint8_t;
+ typedef __int64 int64_t;
+ typedef __int32 int32_t;
+ typedef __int16 int16_t;
+
+#ifdef _M_IX86
+ typedef int64_t intptr2_t;
+#elif defined(_M_IX64)
+ typedef unsigned __int128 uint128_t;
+ typedef __int128 int128_t;
+ typedef int128_t intptr2_t
+#endif
+#else
+#include <stdint.h>
+#include <stddef.h>
+#include <inttypes.h>
+#endif
+
+ #ifndef GUID_DEFINED
+ #define GUID_DEFINED
+
+ typedef struct _GUID
+ {
+ uint32_t Data1;
+ uint16_t Data2;
+ uint16_t Data3;
+ uint8_t Data4[8];
+ } GUID;
+/*
+#ifndef _REFCLSID_DEFINED
+#define REFGUID const GUID &
+#define _REFCLSID_DEFINED
+#endif
+*/
+#endif
+
+// this is for GUID == and !=
+#include <objbase.h>
+#ifndef GUID_EQUALS_DEFINED
+ #define GUID_EQUALS_DEFINED
+#endif
+
+ typedef SSIZE_T ssize_t;
+#ifdef NULL
+ #undef NULL
+#endif
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#ifdef _WIN32_WCE
+typedef int intptr_t;
+#endif
diff --git a/Src/replicant/foundation/win-x86/atomics.h b/Src/replicant/foundation/win-x86/atomics.h
new file mode 100644
index 000000000..f61f46d5c
--- /dev/null
+++ b/Src/replicant/foundation/win-x86/atomics.h
@@ -0,0 +1,61 @@
+#pragma once
+#include "../../foundation/types.h"
+#include <Windows.h>
+#include <intrin.h>
+
+#ifdef __cplusplus
+#define NX_ATOMIC_INLINE inline
+#else
+#define NX_ATOMIC_INLINE
+#endif
+
+NX_ATOMIC_INLINE static size_t nx_atomic_inc(volatile size_t *addr)
+{
+ return (size_t)_InterlockedIncrement((volatile LONG *)addr);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_dec(volatile size_t *addr)
+{
+ return (size_t)_InterlockedDecrement((volatile LONG *)addr);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_dec_release(volatile size_t *addr)
+{
+ return (size_t)_InterlockedDecrement((volatile LONG *)addr);
+}
+
+NX_ATOMIC_INLINE static void nx_atomic_write(size_t value, volatile size_t *addr)
+{
+ InterlockedExchange((LONG *)addr, value);
+}
+
+NX_ATOMIC_INLINE static void nx_atomic_write_pointer(void *value, void* volatile *addr)
+{
+ InterlockedExchangePointer(addr, value);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_add(size_t value, volatile size_t* addr)
+{
+ return (size_t)InterlockedExchangeAdd((volatile LONG *)addr, (LONG)value);
+}
+
+NX_ATOMIC_INLINE static size_t nx_atomic_sub(size_t value, volatile size_t* addr)
+{
+ return (size_t)InterlockedExchangeAdd((volatile LONG *)addr, -(LONG)value);
+}
+
+NX_ATOMIC_INLINE static void *nx_atomic_swap_pointer(const void *value, void* volatile *addr)
+{
+ return InterlockedExchangePointer(addr, (PVOID)value);
+}
+
+NX_ATOMIC_INLINE static int nx_atomic_cmpxchg_pointer(void *oldvalue, void *newvalue, void* volatile *addr)
+{
+ return InterlockedCompareExchangePointer(addr, newvalue, oldvalue) == oldvalue;
+}
+
+#pragma intrinsic(_InterlockedCompareExchange64)
+NX_ATOMIC_INLINE static int nx_atomic_cmpxchg2(int64_t oldvalue, int64_t newvalue, volatile int64_t *addr)
+{
+ return _InterlockedCompareExchange64(addr, newvalue, oldvalue) == oldvalue;
+}
diff --git a/Src/replicant/foundation/win-x86/types.h b/Src/replicant/foundation/win-x86/types.h
new file mode 100644
index 000000000..156f009a2
--- /dev/null
+++ b/Src/replicant/foundation/win-x86/types.h
@@ -0,0 +1,98 @@
+#pragma once
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <wchar.h>
+// first, some standard int types
+typedef unsigned int UINT;
+typedef signed int SINT;
+
+typedef unsigned char UCHAR;
+typedef signed char SCHAR;
+
+typedef unsigned long ARGB32;
+typedef unsigned long RGB32;
+
+typedef unsigned long ARGB24;
+typedef unsigned long RGB24;
+
+typedef unsigned short ARGB16;
+typedef unsigned short RGB16;
+
+typedef unsigned long FOURCC;
+
+typedef wchar_t nsxml_char_t;
+typedef wchar_t ns_char_t;
+typedef wchar_t nsfilename_char_t;
+
+typedef int socklen_t;
+
+#if defined(_WIN32) && !defined(__GNUC__)
+#include <stddef.h>
+#if _MSC_VER >= 1600
+#include <stdint.h>
+#else
+ // since windows doesn't have stdint.h
+ typedef unsigned __int64 uint64_t;
+ typedef unsigned __int32 uint32_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int8 uint8_t;
+ typedef signed __int64 int64_t;
+ typedef signed __int32 int32_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int8 int8_t;
+#ifdef _M_IX86
+ typedef int64_t intptr2_t;
+#else if defined(_M_IX64)
+ typedef unsigned __int128 uint128_t;
+ typedef __int128 int128_t;
+ typedef int128_t intptr2_t
+#endif
+#endif
+#else
+#include <stdint.h>
+#include <stddef.h>
+#include <inttypes.h>
+#endif
+
+#ifndef GUID_DEFINED
+#define GUID_DEFINED
+
+ typedef struct _GUID
+ {
+ uint32_t Data1;
+ uint16_t Data2;
+ uint16_t Data3;
+ uint8_t Data4[8];
+ } GUID;
+/*
+#ifndef _REFCLSID_DEFINED
+#define REFGUID const GUID &
+#define _REFCLSID_DEFINED
+#endif
+*/
+#endif
+
+// this is for GUID == and !=
+#include <objbase.h>
+#ifndef GUID_EQUALS_DEFINED
+ #define GUID_EQUALS_DEFINED
+#endif
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+ typedef SSIZE_T ssize_t;
+#endif
+
+#ifdef NULL
+ #undef NULL
+#endif
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#ifdef _WIN32_WCE
+typedef int intptr_t;
+#endif
diff --git a/Src/replicant/http/HTTPPlayback.cpp b/Src/replicant/http/HTTPPlayback.cpp
new file mode 100644
index 000000000..01200e5af
--- /dev/null
+++ b/Src/replicant/http/HTTPPlayback.cpp
@@ -0,0 +1,325 @@
+#include "http/api.h"
+#include "HTTPPlayback.h"
+#include "http/svc_http_demuxer.h"
+#include "service/ifc_servicefactory.h"
+#include <time.h>
+#ifdef _WIN32
+#include "nu/AutoChar.h"
+#endif
+#include "nu/strsafe.h"
+#include "nx/nxsleep.h"
+#ifdef __ANDROID__
+#include <android/log.h> // TODO: replace with generic logging API
+#else
+#define ANDROID_LOG_INFO 0
+#define ANDROID_LOG_ERROR 1
+void __android_log_print(int, const char *, const char *, ...)
+{
+}
+#endif
+#include <time.h>
+
+HTTPPlayback::HTTPPlayback()
+{
+ http=0;
+ demuxer=0;
+}
+
+int HTTPPlayback::Initialize(nx_uri_t url, ifc_player *player)
+{
+ int ret = PlaybackBase::Initialize(url, player);
+ if (ret != NErr_Success)
+ return ret;
+ http=0;
+ demuxer=0;
+ ifc_playback::Retain(); /* the thread needs to hold a reference to this object so that it doesn't disappear out from under us */
+ NXThreadCreate(&playback_thread, HTTPPlayerThreadFunction, this);
+ return NErr_Success;
+}
+
+HTTPPlayback::~HTTPPlayback()
+{
+ if (demuxer)
+ demuxer->Release();
+ if (http)
+ jnl_http_release(http);
+}
+
+nx_thread_return_t HTTPPlayback::HTTPPlayerThreadFunction(nx_thread_parameter_t param)
+{
+ HTTPPlayback *playback = (HTTPPlayback *)param;
+ NXThreadCurrentSetPriority(NX_THREAD_PRIORITY_PLAYBACK);
+ nx_thread_return_t ret = playback->DecodeLoop();
+ playback->ifc_playback::Release();
+ return ret;
+}
+
+int HTTPPlayback::Init()
+{
+ http = jnl_http_create(2*1024*1024, 0);
+ if (!http)
+ return NErr_OutOfMemory;
+
+ return NErr_Success;
+}
+
+static void SetupHTTP(jnl_http_t http)
+{
+ char accept[1024], user_agent[256];
+ accept[0]=0;
+ user_agent[0]=0;
+ size_t accept_length=sizeof(accept)/sizeof(*accept);
+ size_t user_agent_length=sizeof(user_agent)/sizeof(*user_agent);
+ char *p_accept = accept, *p_user_agent=user_agent;
+
+ const char *application_user_agent = WASABI2_API_APP->GetUserAgent();
+ StringCchCopyExA(p_user_agent, user_agent_length, application_user_agent, &p_user_agent, &user_agent_length, 0);
+
+ GUID http_demuxer_guid = svc_http_demuxer::GetServiceType();
+ ifc_serviceFactory *sf;
+ size_t n = 0;
+ while (sf = WASABI2_API_SVC->EnumService(http_demuxer_guid, n++))
+ {
+ svc_http_demuxer *l = (svc_http_demuxer*)sf->GetInterface();
+ if (l)
+ {
+ const char *this_accept;
+ size_t i=0;
+ while (this_accept=l->EnumerateAcceptedTypes(i++))
+ {
+ if (accept == p_accept) // first one added
+ StringCchCopyExA(p_accept, accept_length, this_accept, &p_accept, &accept_length, 0);
+ else
+ StringCchPrintfExA(p_accept, accept_length, &p_accept, &accept_length, 0, ", %s", this_accept);
+ }
+
+ const char *this_user_agent = l->GetUserAgent();
+ if (this_user_agent)
+ {
+ StringCchPrintfExA(p_user_agent, user_agent_length, &p_user_agent, &user_agent_length, 0, " %s", this_user_agent);
+ }
+
+ l->CustomizeHTTP(http);
+ l->Release();
+ }
+ }
+ if (accept != p_accept)
+ jnl_http_addheadervalue(http, "Accept", accept);
+ jnl_http_addheadervalue(http, "User-Agent", user_agent);
+ jnl_http_addheadervalue(http, "Connection", "close");
+
+}
+
+static NError FindDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer)
+{
+ GUID http_demuxer_guid = svc_http_demuxer::GetServiceType();
+ ifc_serviceFactory *sf;
+
+ bool again;
+ int pass=0;
+ do
+ {
+ size_t n = 0;
+ again=false;
+ while (sf = WASABI2_API_SVC->EnumService(http_demuxer_guid, n++))
+ {
+ svc_http_demuxer *l = (svc_http_demuxer*)sf->GetInterface();
+ if (l)
+ {
+ NError err = l->CreateDemuxer(uri, http, demuxer, pass);
+ if (err == NErr_Success)
+ return NErr_Success;
+
+ if (err == NErr_TryAgain)
+ again=true;
+ }
+ }
+ pass++;
+ } while (again);
+ return NErr_NoMatchingImplementation;
+}
+
+int HTTPPlayback::Internal_Connect(uint64_t byte_position)
+{
+ int http_ver = byte_position?1:0;
+
+ if (byte_position != 0)
+ {
+ char str[512];
+ StringCchPrintfA(str, 512, "Range: bytes=%llu-", byte_position);
+
+ jnl_http_addheader(http, str);
+ }
+ //jnl_http_allow_accept_all_reply_codes(http);
+#ifdef _WIN32
+ jnl_http_connect(http, AutoChar(filename->string), http_ver, "GET");
+#else
+ jnl_http_connect(http, filename->string, http_ver, "GET");
+#endif
+
+ /* wait for connection */
+ time_t start_time = time(0);
+
+ int http_status;
+ do
+ {
+ int ret = PlaybackBase::Sleep(10, PlaybackBase::WAKE_STOP);
+ if (ret == PlaybackBase::WAKE_STOP)
+ return NErr_Interrupted;
+
+ ret = jnl_http_run(http);
+ if (ret == HTTPGET_RUN_ERROR)
+ return NErr_ConnectionFailed;
+ if (start_time + 15 < time(0))
+ return NErr_TimedOut;
+
+ http_status = jnl_http_get_status(http);
+ } while (http_status == HTTPGET_STATUS_CONNECTING || http_status == HTTPGET_STATUS_READING_HEADERS);
+
+ if (http_status == HTTPGET_STATUS_ERROR)
+ {
+ switch(jnl_http_getreplycode(http))
+ {
+ case 400:
+ return NErr_BadRequest;
+ case 401:
+ // TODO: deal with this specially
+ return NErr_Unauthorized;
+ case 403:
+ // TODO: deal with this specially?
+ return NErr_Forbidden;
+ case 404:
+ return NErr_NotFound;
+ case 405:
+ return NErr_BadMethod;
+ case 406:
+ return NErr_NotAcceptable;
+ case 407:
+ // TODO: deal with this specially
+ return NErr_ProxyAuthenticationRequired;
+ case 408:
+ return NErr_RequestTimeout;
+ case 409:
+ return NErr_Conflict;
+ case 410:
+ return NErr_Gone;
+ case 500:
+ return NErr_InternalServerError;
+ case 503:
+ return NErr_ServiceUnavailable;
+
+ default:
+ return NErr_ConnectionFailed;
+
+ }
+ }
+ return NErr_Success;
+}
+
+nx_thread_return_t HTTPPlayback::DecodeLoop()
+{
+ player->OnLoaded(filename);
+
+ int ret = Init();
+ if (ret != NErr_Success)
+ {
+ player->OnError(ret);
+ return 0;
+ }
+
+ SetupHTTP(http);
+
+ /* connect, then find an ifc_http_demuxer */
+ ret = Internal_Connect(0);
+
+ if (ret == NErr_Success && FindDemuxer(filename, http, &demuxer) == NErr_Success && demuxer)
+ {
+ /* turn control over to the demuxer */
+ ret = demuxer->Run(this, player, secondary_parameters);
+ if (ret == NErr_EndOfFile)
+ {
+ /* TODO: re-implement the individual demuxers so they keep calling set position for a while */
+ player->OnClosed();
+ return 0;
+ }
+ }
+ else if (ret == NErr_Interrupted)
+ {
+ player->OnStopped();
+ return 0;
+ }
+ else if (ret == NErr_TimedOut)
+ {
+ player->OnError(ret);
+ return 0;
+ }
+ else if (ret == NErr_Success)
+ {
+ player->OnError(NErr_NoMatchingImplementation);
+ return 0;
+ }
+ else
+ {
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[http] error: %d, reply code: %d", ret, jnl_http_getreplycode(http));
+ player->OnError(ret);
+ return 0;
+ }
+
+ return 0;
+}
+
+int HTTPPlayback::HTTP_Wake(int mask)
+{
+ return PlaybackBase::Wake(mask);
+}
+
+int HTTPPlayback::HTTP_Check(int mask)
+{
+ return PlaybackBase::Check(mask);
+}
+
+int HTTPPlayback::HTTP_Wait(unsigned int milliseconds, int mask)
+{
+ return PlaybackBase::Wait(milliseconds, mask);
+}
+
+int HTTPPlayback::HTTP_Sleep(int milliseconds, int mask)
+{
+ return PlaybackBase::Sleep(milliseconds, mask);
+}
+
+Agave_Seek *HTTPPlayback::HTTP_GetSeek()
+{
+ return PlaybackBase::GetSeek();
+}
+
+void HTTPPlayback::HTTP_FreeSeek(Agave_Seek *seek)
+{
+ PlaybackBase::FreeSeek(seek);
+}
+
+int HTTPPlayback::HTTP_Seek(uint64_t byte_position)
+{
+ jnl_http_reset_headers(http);
+ SetupHTTP(http);
+ return Internal_Connect(byte_position);
+}
+#if defined(_WIN32) && !defined(strcasecmp)
+#define strcasecmp _stricmp
+#endif
+
+int HTTPPlayback::HTTP_Seekable()
+{
+ const char *accept_ranges = jnl_http_getheader(http, "accept-ranges");
+ if (accept_ranges && !strcasecmp(accept_ranges, "none"))
+ return NErr_False; /* server says it doesn't accept ranges */
+
+ /* note that not having an accept-ranges header doesn't necessary mean it's not seekable. see RFC2616 14.5 */
+ return NErr_True;
+}
+
+int HTTPPlayback::HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output)
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[http] output_service=%x", output_service);
+ return output_service->AudioOpen(format, player, secondary_parameters, out_output);
+}
diff --git a/Src/replicant/http/HTTPPlayback.h b/Src/replicant/http/HTTPPlayback.h
new file mode 100644
index 000000000..12082b05c
--- /dev/null
+++ b/Src/replicant/http/HTTPPlayback.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "nx/nxonce.h"
+#include "jnetlib/jnetlib.h"
+#include "http/ifc_http_demuxer.h"
+#include "http/ifc_http.h"
+#include "nswasabi/PlaybackBase.h"
+
+class HTTPPlayback : public PlaybackBase, public ifc_http
+{
+public:
+ HTTPPlayback();
+ ~HTTPPlayback();
+
+ int Initialize(nx_uri_t url, ifc_player *player);
+ /* ifc_http implementation */
+ int WASABICALL HTTP_Wake(int mask);
+ int WASABICALL HTTP_Check(int mask);
+ int WASABICALL HTTP_Wait(unsigned int milliseconds, int mask);
+ int WASABICALL HTTP_Sleep(int milliseconds, int mask);
+ Agave_Seek *WASABICALL HTTP_GetSeek();
+ void WASABICALL HTTP_FreeSeek(Agave_Seek *seek);
+ int WASABICALL HTTP_Seek(uint64_t byte_position);
+ int WASABICALL HTTP_Seekable();
+ int WASABICALL HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output);
+private:
+ int Internal_Connect(uint64_t byte_position);
+ ifc_http_demuxer *demuxer;
+
+ volatile int paused;
+ volatile int stopped;
+ jnl_http_t http;
+
+ int Init();
+
+ nx_thread_return_t NXTHREADCALL DecodeLoop();
+ static nx_thread_return_t NXTHREADCALL HTTPPlayerThreadFunction(nx_thread_parameter_t param);
+};
diff --git a/Src/replicant/http/HTTPPlaybackService.cpp b/Src/replicant/http/HTTPPlaybackService.cpp
new file mode 100644
index 000000000..cd4d5c76c
--- /dev/null
+++ b/Src/replicant/http/HTTPPlaybackService.cpp
@@ -0,0 +1,28 @@
+#include "HTTPPlaybackService.h"
+#include "player/ifc_player.h"
+#include "player/ifc_playback.h"
+#include "HTTPPlayback.h"
+#include "nx/nxpath.h"
+#include "nswasabi/ReferenceCounted.h"
+
+int HTTPPlaybackService::PlaybackService_CreatePlayback(unsigned int pass, nx_uri_t filename, ifc_player *player, ifc_playback **out_playback_object)
+{
+ if (NXPathProtocol(filename, "http") == NErr_Success)
+ {
+ HTTPPlayback *http_playback = new ReferenceCounted<HTTPPlayback>;
+ if (!http_playback)
+ return NErr_OutOfMemory;
+
+ int ret = http_playback->Initialize(filename, player);
+ if (ret != NErr_Success)
+ {
+ http_playback->ifc_playback::Release();
+ return ret;
+ }
+
+ *out_playback_object = http_playback;
+ return NErr_Success;
+ }
+ return NErr_False;
+}
+
diff --git a/Src/replicant/http/HTTPPlaybackService.h b/Src/replicant/http/HTTPPlaybackService.h
new file mode 100644
index 000000000..b048ce149
--- /dev/null
+++ b/Src/replicant/http/HTTPPlaybackService.h
@@ -0,0 +1,16 @@
+#pragma once
+#include "player/svc_playback.h"
+#include "nx/nxstring.h"
+#include "nswasabi/ServiceName.h"
+
+// {672AF800-F239-40e5-8C87-3B4D305B72B2}
+static const GUID http_playback_guid =
+{ 0x672af800, 0xf239, 0x40e5, { 0x8c, 0x87, 0x3b, 0x4d, 0x30, 0x5b, 0x72, 0xb2 } };
+
+class HTTPPlaybackService : public svc_playback
+{
+public:
+ WASABI_SERVICE_NAME("HTTP Playback Service");
+ static GUID GetServiceGUID() { return http_playback_guid; }
+ int WASABICALL PlaybackService_CreatePlayback(unsigned int pass, nx_uri_t filename, ifc_player *player, ifc_playback **out_playback_object);
+}; \ No newline at end of file
diff --git a/Src/replicant/http/api.h b/Src/replicant/http/api.h
new file mode 100644
index 000000000..eb87b2491
--- /dev/null
+++ b/Src/replicant/http/api.h
@@ -0,0 +1,8 @@
+#pragma once
+#include "service/api_service.h"
+extern api_service *serviceApi;
+#define WASABI2_API_SVC serviceApi
+
+#include "application/api_application.h"
+extern api_application *applicationApi;
+#define WASABI2_API_APP applicationApi
diff --git a/Src/replicant/http/http.vcxproj b/Src/replicant/http/http.vcxproj
new file mode 100644
index 000000000..751e9d466
--- /dev/null
+++ b/Src/replicant/http/http.vcxproj
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{B6B8BAE5-BC2C-4A78-97C3-D0A5053F11F2}</ProjectGuid>
+ <RootNamespace>http</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x86_Debug\</OutDir>
+ <IntDir>x86_Debug\</IntDir>
+ <TargetExt>.w5c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x64_Debug\</OutDir>
+ <IntDir>x64_Debug\</IntDir>
+ <TargetExt>.w5c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x86_Release\</OutDir>
+ <IntDir>x86_Release\</IntDir>
+ <TargetExt>.w5c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x64_Release\</OutDir>
+ <IntDir>x64_Release\</IntDir>
+ <TargetExt>.w5c</TargetExt>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;HTTP_EXPORTS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <CustomBuildStep>
+ <Command>copy "$(TargetPath)" "$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)"</Command>
+ <Outputs>$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)</Outputs>
+ <Inputs>$(TargetPath);%(Inputs)</Inputs>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;HTTP_EXPORTS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <CustomBuildStep>
+ <Command>copy "$(TargetPath)" "$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)"</Command>
+ <Outputs>$(ProgramFiles)\Replicant\$(TargetName)$(TargetExt)</Outputs>
+ <Inputs>$(TargetPath);%(Inputs)</Inputs>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="HTTPPlayback.cpp" />
+ <ClCompile Include="HTTPPlaybackService.cpp" />
+ <ClCompile Include="main.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h" />
+ <ClInclude Include="HTTPPlayback.h" />
+ <ClInclude Include="HTTPPlaybackService.h" />
+ <ClInclude Include="ifc_http.h" />
+ <ClInclude Include="ifc_http_demuxer.h" />
+ <ClInclude Include="svc_http_demuxer.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\jnetlib\jnetlib.vcxproj">
+ <Project>{e105a0a2-7391-47c5-86ac-718003524c3d}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\nswasabi\nswasabi.vcxproj">
+ <Project>{480502a0-71da-4bf0-bf99-2720d69a526b}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\nu\nu.vcxproj">
+ <Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\nx\nx.vcxproj">
+ <Project>{2851cf33-337d-44d9-ba6d-30547b1cdef0}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/http/ifc_http.h b/Src/replicant/http/ifc_http.h
new file mode 100644
index 000000000..1e34906f4
--- /dev/null
+++ b/Src/replicant/http/ifc_http.h
@@ -0,0 +1,68 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "player/ifc_playback.h"
+#include "foundation/types.h"
+// TODO: benski> not sure that this is the best name for it, but it works for now
+
+class ifc_http : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_http() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_http() {}
+public:
+
+ enum
+ {
+ WAKE_KILL=(1<<0),
+ WAKE_PLAY=(1<<1),
+ WAKE_PAUSE=(1<<2),
+ WAKE_STOP=(1<<3),
+ WAKE_INTERRUPT=(1<<4),
+ WAKE_UNPAUSE=(1<<5), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason
+ WAKE_RESUME=(1<<6), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason
+ WAKE_START_MASK = WAKE_PLAY|WAKE_STOP,
+ WAKE_KILL_MASK = WAKE_KILL|WAKE_STOP,
+ WAKE_ALL_MASK = WAKE_KILL|WAKE_PLAY|WAKE_PAUSE|WAKE_STOP|WAKE_INTERRUPT,
+ };
+
+ // these aren't the best names, either
+
+ // if playback flag isn't ready, sleeps until a flag changes and returns changed flag
+ int Wake(int mask) { return HTTP_Wake(mask); }
+
+ // checks for pending flags and updates them
+ int Check(int mask) { return HTTP_Check(mask); }
+
+ // like wake, but only wait a specified amount of time. will return 0 if flags didn't change
+ int Wait(unsigned int milliseconds, int mask) { return HTTP_Wait(milliseconds, mask); }
+
+ int Sleep(unsigned int milliseconds, int mask) { return HTTP_Sleep(milliseconds, mask); }
+
+ Agave_Seek *GetSeek() { return HTTP_GetSeek(); }
+ void FreeSeek(Agave_Seek *seek) { HTTP_FreeSeek(seek); }
+
+ int Seek(uint64_t byte_position) { return HTTP_Seek(byte_position); }
+
+ /* returns NErr_True / NErr_False, returns whether or not it's seekable by Range headers.
+ NErr_True doesn't mean 100% certainity that the stream is seekable.
+ Note that some protocols (e.g. RTSP) might still be seekable by other means than Range headers. */
+ int Seekable() { return HTTP_Seekable(); }
+
+ int AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output) { return HTTP_AudioOpen(format, out_output); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual int WASABICALL HTTP_Wake(int mask)=0;
+ virtual int WASABICALL HTTP_Check(int mask)=0;
+ virtual int WASABICALL HTTP_Wait(unsigned int milliseconds, int mask)=0;
+ virtual int WASABICALL HTTP_Sleep(int milliseconds, int mask)=0;
+ virtual Agave_Seek *WASABICALL HTTP_GetSeek()=0;
+ virtual void WASABICALL HTTP_FreeSeek(Agave_Seek *seek)=0;
+ virtual int WASABICALL HTTP_Seek(uint64_t byte_position)=0;
+ virtual int WASABICALL HTTP_Seekable()=0;
+ virtual int WASABICALL HTTP_AudioOpen(const ifc_audioout::Parameters *format, ifc_audioout **out_output)=0;
+
+};
diff --git a/Src/replicant/http/ifc_http_demuxer.h b/Src/replicant/http/ifc_http_demuxer.h
new file mode 100644
index 000000000..4616a6ea9
--- /dev/null
+++ b/Src/replicant/http/ifc_http_demuxer.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "jnetlib/jnetlib_defines.h"
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "player/svc_output.h"
+#include "player/ifc_player.h"
+#include "http/ifc_http.h"
+#include "player/ifc_playback_parameters.h"
+
+class ifc_http_demuxer: public Wasabi2::Dispatchable
+{
+protected:
+ ifc_http_demuxer() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_http_demuxer() {}
+public:
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+ int Run(ifc_http *http_parent, ifc_player *player, ifc_playback_parameters *secondary_parameters) { return HTTPDemuxer_Run(http_parent, player, secondary_parameters); }
+
+protected:
+ virtual int WASABICALL HTTPDemuxer_Run(ifc_http *http_parent, ifc_player *player, ifc_playback_parameters *secondary_parameters)=0;
+};
diff --git a/Src/replicant/http/main.cpp b/Src/replicant/http/main.cpp
new file mode 100644
index 000000000..3e146df58
--- /dev/null
+++ b/Src/replicant/http/main.cpp
@@ -0,0 +1,68 @@
+#include "api.h"
+#include "jnetlib/jnetlib.h"
+#include "component/ifc_component.h"
+#include "service/ifc_servicefactory.h"
+#include "foundation/export.h"
+#include "nswasabi/singleton.h"
+#include "HTTPPlaybackService.h"
+
+static SingletonService<HTTPPlaybackService, svc_playback> playback_factory;
+
+// {446BFBF6-8CE9-4697-844E-8386B5037685}
+static const GUID http_component_guid =
+{ 0x446bfbf6, 0x8ce9, 0x4697, { 0x84, 0x4e, 0x83, 0x86, 0xb5, 0x3, 0x76, 0x85 } };
+
+
+class HTTPComponent : public ifc_component
+{
+public:
+ HTTPComponent() : ifc_component(http_component_guid) {}
+ int WASABICALL Component_Initialize(api_service *service);
+ int WASABICALL Component_RegisterServices(api_service *service);
+
+ void WASABICALL Component_DeregisterServices(api_service *service);
+ int WASABICALL Component_Quit(api_service *_service_manager);
+};
+
+static HTTPComponent http_component;
+api_service *WASABI2_API_SVC=0;
+api_application *WASABI2_API_APP=0;
+
+int HTTPComponent::Component_Initialize(api_service *service)
+{
+ int ret = jnl_init();
+ if (ret != NErr_Success)
+ return ret;
+
+ return NErr_Success;
+}
+
+int HTTPComponent::Component_RegisterServices(api_service *service)
+{
+ WASABI2_API_SVC = service;
+
+ // get application API
+ WASABI2_API_SVC->GetService(&WASABI2_API_APP);
+
+ playback_factory.Register(WASABI2_API_SVC);
+ return NErr_Success;
+}
+
+void HTTPComponent::Component_DeregisterServices(api_service *service)
+{
+ playback_factory.Deregister(WASABI2_API_SVC);
+
+ if (WASABI2_API_APP)
+ WASABI2_API_APP->Release();
+}
+
+int HTTPComponent::Component_Quit(api_service *_service_manager)
+{
+ jnl_quit();
+ return NErr_Success;
+}
+
+extern "C" DLLEXPORT ifc_component *GetWasabi2Component()
+{
+ return &http_component;
+}
diff --git a/Src/replicant/http/svc_http_demuxer.h b/Src/replicant/http/svc_http_demuxer.h
new file mode 100644
index 000000000..ee3a5abbf
--- /dev/null
+++ b/Src/replicant/http/svc_http_demuxer.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "jnetlib/jnetlib_defines.h"
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "http/ifc_http_demuxer.h"
+
+// {5E3551B0-B0FF-4997-89E4-958545C3EC19}
+static const GUID demuxer_service_type_guid =
+{ 0x5E3551B0, 0xB0FF, 0x4997, { 0x89, 0xE4, 0x95, 0x85, 0x45, 0xC3, 0xEC, 0x19 } };
+
+class svc_http_demuxer: public Wasabi2::Dispatchable
+{
+protected:
+ svc_http_demuxer() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_http_demuxer() {}
+public:
+ static GUID GetServiceType() { return demuxer_service_type_guid; }
+ /* returns types to be added to "Accept" HTTP header */
+ const char *EnumerateAcceptedTypes(size_t i) { return HTTPDemuxerService_EnumerateAcceptedTypes(i); }
+ /* returns a string to be added to the user-agent (e.g. Ultravox/2.1) */
+ const char *GetUserAgent() { return HTTPDemuxerService_GetUserAgent(); }
+ /* allows service to do any necessary customization (mainly for adding headers) */
+ void CustomizeHTTP(jnl_http_t http) { HTTPDemuxerService_CustomizeHTTP(http); }
+
+ /* if you create a demuxer, you now own http and are expected to call jnl_http_release on it when you are done */
+ /* you can return NErr_TryAgain to let everyone else go first, you'll be called again with pass=1 */
+ NError CreateDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer, int pass) { return HTTPDemuxerService_CreateDemuxer(uri, http, demuxer, pass); }
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+protected:
+ virtual const char *WASABICALL HTTPDemuxerService_EnumerateAcceptedTypes(size_t i) = 0;
+ virtual const char *WASABICALL HTTPDemuxerService_GetUserAgent() = 0;
+ virtual void WASABICALL HTTPDemuxerService_CustomizeHTTP(jnl_http_t http) = 0;
+ virtual NError WASABICALL HTTPDemuxerService_CreateDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer, int pass) = 0;
+};
diff --git a/Src/replicant/icy/ifc_icy_playback.h b/Src/replicant/icy/ifc_icy_playback.h
new file mode 100644
index 000000000..f918ff4ff
--- /dev/null
+++ b/Src/replicant/icy/ifc_icy_playback.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "jnetlib/jnetlib_defines.h"
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "player/svc_output.h"
+#include "player/ifc_player.h"
+#include "http/ifc_http.h"
+#include "icy/ifc_icy_reader.h"
+#include "player/ifc_playback_parameters.h"
+
+class ifc_icy_playback: public Wasabi2::Dispatchable
+{
+protected:
+ ifc_icy_playback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_icy_playback() {}
+public:
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+ int Run(ifc_http *http_parent, ifc_player *player, ifc_icy_reader *reader) { return ICYPlayback_Run(http_parent, player, reader); }
+
+protected:
+ virtual int WASABICALL ICYPlayback_Run(ifc_http *http_parent, ifc_player *player, ifc_icy_reader *reader)=0;
+};
diff --git a/Src/replicant/icy/ifc_icy_reader.h b/Src/replicant/icy/ifc_icy_reader.h
new file mode 100644
index 000000000..74e2d9280
--- /dev/null
+++ b/Src/replicant/icy/ifc_icy_reader.h
@@ -0,0 +1,37 @@
+/*
+ * ifc_icy_reader.h
+ * shoutcast_player
+ *
+ * Created by Ben Allison on 2/1/08.
+ * Copyright 2008 Nullsoft, Inc. All rights reserved.
+ *
+ */
+ #pragma once
+#include "foundation/dispatch.h"
+#include "foundation/types.h"
+#include "foundation/error.h"
+
+class ifc_icy_reader : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_icy_reader() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_icy_reader() {}
+public:
+ size_t BytesBuffered() { return ICYReader_BytesBuffered(); }
+ int Read(void *buffer, size_t length, size_t *readLength) { return ICYReader_Read(buffer, length, readLength); }
+ int Peek(void *buffer, size_t length, size_t *readLength) { return ICYReader_Peek(buffer, length, readLength); }
+ int IsClosed() { return ICYReader_IsClosed(); }
+ int Run() { return ICYReader_Run(); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual size_t WASABICALL ICYReader_BytesBuffered()=0;
+ virtual int WASABICALL ICYReader_Read(void *buffer, size_t length, size_t *readLength)=0;
+ virtual int WASABICALL ICYReader_Peek(void *buffer, size_t length, size_t *readLength)=0;
+ virtual int WASABICALL ICYReader_IsClosed()=0;
+ virtual int WASABICALL ICYReader_Run()=0;
+
+};
diff --git a/Src/replicant/icy/svc_icy_playback.h b/Src/replicant/icy/svc_icy_playback.h
new file mode 100644
index 000000000..79e024352
--- /dev/null
+++ b/Src/replicant/icy/svc_icy_playback.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "jnetlib/jnetlib_defines.h"
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "icy/ifc_icy_playback.h"
+
+
+// {42E078D5-7D68-43b5-9AFD-0135558C799F}
+static const GUID icy_playback_service_type_guid =
+{ 0x42e078d5, 0x7d68, 0x43b5, { 0x9a, 0xfd, 0x1, 0x35, 0x55, 0x8c, 0x79, 0x9f } };
+
+class svc_icy_playback : public Wasabi2::Dispatchable
+{
+protected:
+ svc_icy_playback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_icy_playback() {}
+public:
+ static GUID GetServiceType() { return icy_playback_service_type_guid; }
+ /* returns types to be added to "Accept" HTTP header */
+ const char *EnumerateAcceptedTypes(size_t i) { return ICYPlaybackService_EnumerateAcceptedTypes(i); }
+
+ NError CreatePlayback(jnl_http_t http, ifc_icy_playback **playback, int pass) { return ICYPlaybackService_CreateDemuxer(http, playback, pass); }
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+protected:
+ virtual const char *WASABICALL ICYPlaybackService_EnumerateAcceptedTypes(size_t i) = 0;
+ virtual NError WASABICALL ICYPlaybackService_CreateDemuxer(jnl_http_t http, ifc_icy_playback **playback, int pass) = 0;
+};
diff --git a/Src/replicant/jnetlib/VERSION b/Src/replicant/jnetlib/VERSION
new file mode 100644
index 000000000..ea710abb9
--- /dev/null
+++ b/Src/replicant/jnetlib/VERSION
@@ -0,0 +1 @@
+1.2 \ No newline at end of file
diff --git a/Src/replicant/jnetlib/asyncdns.cpp b/Src/replicant/jnetlib/asyncdns.cpp
new file mode 100644
index 000000000..2a680216d
--- /dev/null
+++ b/Src/replicant/jnetlib/asyncdns.cpp
@@ -0,0 +1,314 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: asyncdns.cpp - JNL portable asynchronous DNS implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "asyncdns.h"
+#include <time.h>
+#ifdef _WIN32
+#include <strsafe.h>
+#endif
+
+enum
+{
+ MODE_RESOLVE=0,
+ MODE_REVERSE=1,
+};
+
+
+struct cache_entry
+{
+ time_t last_used; // timestamp.
+ bool resolved;
+ int mode; // 1=reverse
+ unsigned short port;
+ char hostname[256];
+ addrinfo *addr;
+ int sockettype;
+};
+
+JNL_AsyncDNS::JNL_AsyncDNS(int max_cache_entries)
+{
+ m_thread_kill=1;
+ m_thread=0;
+ m_cache_size=max_cache_entries;
+ m_cache=(cache_entry *)malloc(sizeof(cache_entry)*m_cache_size);
+ memset(m_cache, 0, sizeof(cache_entry)*m_cache_size);
+}
+
+JNL_AsyncDNS::~JNL_AsyncDNS()
+{
+ m_thread_kill=1;
+
+#ifdef _WIN32
+ if (m_thread)
+ {
+ WaitForSingleObject(m_thread,INFINITE);
+ CloseHandle(m_thread);
+ }
+#else
+ if (m_thread)
+ {
+ void *p;
+ pthread_join(m_thread,&p);
+ }
+#endif//!_WIN32
+ // free all the addrinfo stuff
+ for (int x = 0; x < m_cache_size; x ++)
+ {
+ if (m_cache[x].addr)
+ freeaddrinfo(m_cache[x].addr);
+ }
+
+ free(m_cache);
+}
+
+int JNL_AsyncDNS::resolvenow(const char *hostname, unsigned short port, addrinfo **addr, int sockettype)
+{
+ addrinfo hints;
+ memset(&hints,0,sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ if (hostname)
+ hints.ai_flags = AI_NUMERICHOST;
+ else
+ hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+ hints.ai_socktype = sockettype;
+
+ char portString[32] = {0};
+ sprintf(portString, "%u", (unsigned int)port);
+
+ if (getaddrinfo(hostname, portString, &hints, addr) == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ hints.ai_flags = 0;
+ if (getaddrinfo(hostname, portString, &hints, addr) == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+}
+
+#ifdef _WIN32
+unsigned long WINAPI JNL_AsyncDNS::_threadfunc(LPVOID _d)
+#else
+unsigned int JNL_AsyncDNS::_threadfunc(void *_d)
+#endif
+{
+ int nowinsock=JNL::open_socketlib();
+ JNL_AsyncDNS *_this=(JNL_AsyncDNS*)_d;
+ int x;
+ for (x = 0; x < _this->m_cache_size && !_this->m_thread_kill; x ++)
+ {
+ if (_this->m_cache[x].last_used && !_this->m_cache[x].resolved)
+ {
+ if (!nowinsock)
+ {
+ if (_this->m_cache[x].mode==0)
+ {
+ addrinfo *res=0;
+ if (resolvenow(_this->m_cache[x].hostname, _this->m_cache[x].port, &res, _this->m_cache[x].sockettype) == 0)
+ {
+ _this->m_cache[x].addr=res;
+ }
+ else
+ {
+ _this->m_cache[x].addr=0;//INADDR_NONE;
+ }
+ }
+ else if (_this->m_cache[x].mode==1)
+ {
+ /*
+ hostent *ent;
+ // TODO: replace with getnameinfo for IPv6
+ ent=gethostbyaddr((const char *)&_this->m_cache[x].addr,4,AF_INET);
+ if (ent)
+ lstrcpyn(_this->m_cache[x].hostname, ent->h_name, 256);
+ else
+ _this->m_cache[x].hostname[0]=0;
+ */
+ }
+ _this->m_cache[x].resolved=true;
+ }
+ else
+ {
+ if (_this->m_cache[x].mode==0)
+ {
+ _this->m_cache[x].addr=0;//INADDR_NONE;
+ _this->m_cache[x].resolved=true;
+ }
+ else if (_this->m_cache[x].mode==1)
+ {
+ _this->m_cache[x].hostname[0]=0;
+ _this->m_cache[x].resolved=true;
+ }
+ }
+ }
+ }
+ if (!nowinsock) JNL::close_socketlib();
+ _this->m_thread_kill=1;
+
+ return 0;
+}
+
+int JNL_AsyncDNS::resolve(const char *hostname, unsigned short port, addrinfo **addr, int sockettype)
+{
+ // return 0 on success, 1 on wait, -1 on unresolvable
+ int x;
+
+ for (x = 0; x < m_cache_size; x ++)
+ {
+ if (!strcasecmp(m_cache[x].hostname,hostname) && port == m_cache[x].port && m_cache[x].mode==0 && m_cache[x].sockettype==sockettype)
+ {
+ m_cache[x].last_used=time(0);
+ if (m_cache[x].resolved)
+ {
+ if (m_cache[x].addr == 0)//INADDR_NONE)
+ {
+ return DNS_RESOLVE_UNRESOLVABLE;
+ }
+ *addr =m_cache[x].addr;
+ return DNS_RESOLVE_SUCCESS;
+ }
+ makesurethreadisrunning();
+ return DNS_RESOLVE_WAIT;
+ }
+ }
+ // add to resolve list
+ int oi=-1;
+ for (x = 0; x < m_cache_size; x ++)
+ {
+ if (!m_cache[x].last_used)
+ {
+ oi=x;
+ break;
+ }
+ if ((oi==-1 || m_cache[x].last_used < m_cache[oi].last_used) && m_cache[x].resolved)
+ {
+ oi=x;
+ }
+ }
+ if (oi == -1)
+ {
+ return DNS_RESOLVE_UNRESOLVABLE;
+ }
+#ifdef _WIN32
+ StringCchCopyA(m_cache[oi].hostname, 256, hostname);
+#elif defined(__APPLE__)
+ strlcpy(m_cache[oi].hostname, hostname, 255);
+#else
+ strncpy(m_cache[oi].hostname, hostname, 255);
+ m_cache[oi].hostname[255]=0;
+#endif
+ m_cache[oi].port=port;
+ m_cache[oi].mode=0;
+ m_cache[oi].addr=0;//INADDR_NONE;
+ m_cache[oi].resolved=false;
+ m_cache[oi].last_used=time(0);
+ m_cache[oi].sockettype=sockettype;
+
+ makesurethreadisrunning();
+ return DNS_RESOLVE_WAIT;
+}
+
+/*
+int JNL_AsyncDNS::reverse(unsigned long addr, char *hostname, size_t hostnameSize)
+{
+// return 0 on success, 1 on wait, -1 on unresolvable
+int x;
+if (addr == INADDR_NONE)
+{
+return DNS_REVERSE_UNRESOLVABLE;
+}
+#ifndef NO_DNS_SUPPORT
+for (x = 0; x < m_cache_size; x ++)
+{
+if (m_cache[x].addr==addr && m_cache[x].mode==1)
+{
+m_cache[x].last_used=time(0);
+if (m_cache[x].resolved)
+{
+if (!m_cache[x].hostname[0])
+{
+return DNS_REVERSE_UNRESOLVABLE;
+}
+lstrcpyn(hostname,m_cache[x].hostname, hostnameSize);
+return DNS_REVERSE_SUCCESS;
+}
+makesurethreadisrunning();
+return DNS_REVERSE_WAIT;
+}
+}
+// add to resolve list
+int oi=-1;
+for (x = 0; x < m_cache_size; x ++)
+{
+if (!m_cache[x].last_used)
+{
+oi=x;
+break;
+}
+if ((oi==-1 || m_cache[x].last_used < m_cache[oi].last_used) && m_cache[x].resolved)
+{
+oi=x;
+}
+}
+if (oi == -1)
+{
+return DNS_REVERSE_UNRESOLVABLE;
+}
+m_cache[oi].addr=addr;
+m_cache[oi].hostname[0]=0;
+m_cache[oi].resolved=false;
+m_cache[oi].mode=1;
+m_cache[oi].last_used=time(0);
+
+makesurethreadisrunning();
+return DNS_REVERSE_WAIT;
+#else
+return DNS_REVERSE_UNRESOLVABLE;
+#endif
+}
+*/
+
+void JNL_AsyncDNS::makesurethreadisrunning(void)
+{
+ if (m_thread_kill)
+ {
+#ifdef _WIN32
+ if (m_thread)
+ {
+ WaitForSingleObject(m_thread,INFINITE);
+ CloseHandle(m_thread);
+ }
+ DWORD id;
+ m_thread_kill=0;
+ m_thread=CreateThread(NULL,0,_threadfunc,(LPVOID)this,0,&id);
+ if (!m_thread)
+ {
+#else
+ if (m_thread)
+ {
+ void *p;
+ pthread_join(m_thread,&p);
+ }
+ m_thread_kill=0;
+ if (pthread_create(&m_thread,NULL,(void *(*) (void *))_threadfunc,(void*)this) != 0)
+ {
+#endif
+ m_thread_kill=1;
+ }
+ }
+}
+
diff --git a/Src/replicant/jnetlib/asyncdns.h b/Src/replicant/jnetlib/asyncdns.h
new file mode 100644
index 000000000..b346eccd7
--- /dev/null
+++ b/Src/replicant/jnetlib/asyncdns.h
@@ -0,0 +1,66 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: asyncdns.h - JNL portable asynchronous DNS interface
+** License: see jnetlib.h
+**
+** Usage:
+** 1. Create JNL_AsyncDNS object, optionally with the number of cache entries.
+** 2. call resolve() to resolve a hostname into an address. The return value of
+** resolve is 0 on success (host successfully resolved), 1 on wait (meaning
+** try calling resolve() with the same hostname in a few hundred milliseconds
+** or so), or -1 on error (i.e. the host can't resolve).
+** 3. call reverse() to do reverse dns (ala resolve()).
+** 4. enjoy.
+*/
+
+#ifndef _ASYNCDNS_H_
+#define _ASYNCDNS_H_
+
+#include "netinc.h"
+
+struct cache_entry;
+
+#define JNL_AUTODNS ((JNL_AsyncDNS *)-1)
+enum
+{
+ DNS_RESOLVE_UNRESOLVABLE = -1,
+ DNS_RESOLVE_SUCCESS = 0,
+ DNS_RESOLVE_WAIT = 1,
+};
+
+enum
+{
+ DNS_REVERSE_UNRESOLVABLE = -1,
+ DNS_REVERSE_SUCCESS = 0,
+ DNS_REVERSE_WAIT = 1,
+};
+
+class JNL_AsyncDNS
+{
+public:
+ JNL_AsyncDNS( int max_cache_entries = 64 );
+ ~JNL_AsyncDNS();
+
+ int resolve( const char *hostname, unsigned short port, addrinfo **addr, int sockettype ); // return 0 on success, 1 on wait, -1 on unresolvable
+ static int resolvenow( const char *hostname, unsigned short port, addrinfo **addr, int sockettype ); // return 0 on success, -1 on unresolvable
+ //int reverse(unsigned long addr, char *hostname, size_t hostnameSize); // return 0 on success, 1 on wait, -1 on unresolvable. hostname must be at least 256 bytes.
+
+private:
+ cache_entry *m_cache;
+ int m_cache_size;
+ volatile int m_thread_kill;
+
+#ifdef _WIN32
+ HANDLE m_thread;
+ static unsigned long WINAPI _threadfunc( LPVOID _d );
+#else
+ pthread_t m_thread;
+ static unsigned int _threadfunc( void *_d );
+#endif
+
+ void makesurethreadisrunning( void );
+};
+
+#endif //_ASYNCDNS_H_
diff --git a/Src/replicant/jnetlib/connection.cpp b/Src/replicant/jnetlib/connection.cpp
new file mode 100644
index 000000000..4aacdd471
--- /dev/null
+++ b/Src/replicant/jnetlib/connection.cpp
@@ -0,0 +1,533 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: connection.cpp - JNL TCP connection implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "connection.h"
+#include "asyncdns.h"
+#include "foundation\error.h"
+
+
+#ifndef min
+#define min(X,Y) ((X) < (Y) ? (X) : (Y))
+#endif
+
+JNL_Connection::JNL_Connection()
+{
+ init();
+}
+
+JNL_Connection::JNL_Connection(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize)
+{
+ init();
+ open(dns, sendbufsize, recvbufsize);
+}
+
+
+void JNL_Connection::init()
+{
+ m_errorstr="";
+ address=0;
+ m_dns=0;
+ m_dns_owned=false;
+ m_socket=-1;
+ m_remote_port=0;
+ m_state=STATE_NOCONNECTION;
+ m_host[0]=0;
+ saddr=0;
+}
+
+JNL_Connection::~JNL_Connection()
+{
+ /*
+ ** Joshua Teitelbaum 1/27/2006
+ ** virtualization for ssl, calling socket_shtudown()
+ */
+ socket_shutdown();
+
+ if (!saddr) // free it if it was passed to us (by JNL_Listen, presumably)
+ free(address); // TODO: change this if we ever do round-robin DNS connecting or in any way change how we handle 'address'
+
+ if (m_dns_owned)
+ delete m_dns;
+}
+
+void JNL_Connection::set_dns(JNL_AsyncDNS *dns)
+{
+ if (m_dns_owned)
+ delete static_cast<JNL_AsyncDNS *>(m_dns);
+
+ m_dns=dns;
+ m_dns_owned=false;
+}
+
+void JNL_Connection::open(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize)
+{
+ if (dns != JNL_AUTODNS && dns)
+ {
+ m_dns=dns;
+ m_dns_owned=false;
+ }
+ else if (!m_dns)
+ {
+ m_dns=new JNL_AsyncDNS;
+ m_dns_owned=true;
+ }
+
+ recv_buffer.reserve(recvbufsize);
+ send_buffer.reserve(sendbufsize);
+}
+
+void JNL_Connection::connect(SOCKET s, sockaddr *addr, socklen_t length)
+{
+ close(1);
+ m_socket=s;
+ address=(sockaddr *)malloc(length);
+ memcpy(address, addr, length);
+
+ m_remote_port=0;
+ if (m_socket != -1)
+ {
+ SET_SOCK_BLOCK(m_socket,0);
+ m_state=STATE_CONNECTED;
+ }
+ else
+ {
+ m_errorstr="invalid socket passed to connect";
+ m_state=STATE_ERROR;
+ }
+
+}
+
+void JNL_Connection::connect(const char *hostname, int port)
+{
+ close(1);
+ m_remote_port=(unsigned short)port;
+
+#ifdef _WIN32
+ lstrcpynA(m_host, hostname, sizeof(m_host));
+#elif defined(__APPLE__)
+ strlcpy(m_host, hostname, sizeof(m_host));
+#else
+ strncpy(m_host, hostname, sizeof(m_host)-1);
+ m_host[sizeof(m_host)-1]=0;
+#endif
+
+
+ //memset(&m_saddr,0,sizeof(m_saddr));
+ if (!m_host[0])
+ {
+ m_errorstr="empty hostname";
+ m_state=STATE_ERROR;
+ }
+ else
+ {
+ m_state=STATE_RESOLVING;
+ }
+}
+
+/*
+** Joshua Teitelbaum 1/27/2006
+** socket_shutdown
+** virtualization for ssl
+*/
+/* Virtual */
+void JNL_Connection::socket_shutdown()
+{
+ if (m_socket >= 0)
+ {
+ ::shutdown(m_socket, SHUT_RDWR);
+ ::closesocket(m_socket);
+
+ m_socket=-1;
+ }
+}
+/*
+** Joshua Teitelbaum 1/27/2006
+** socket_recv
+** virtualization for ssl
+*/
+/* Virtual */
+ssize_t JNL_Connection::socket_recv(char *buf, size_t len, int options)
+{
+ return ::recv(m_socket,buf,(int)len,options);
+}
+/*
+** Joshua Teitelbaum 1/27/2006
+** socket_send
+** virtualization for ssl
+*/
+/* Virtual */
+ssize_t JNL_Connection::socket_send(const char *buf, size_t len, int options)
+{
+ return ::send(m_socket,buf,(int)len,options);
+}
+
+int JNL_Connection::socket_connect()
+{
+ return ::connect(m_socket, saddr->ai_addr, (int)saddr->ai_addrlen);
+}
+
+void JNL_Connection::run(size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd)
+{
+ socklen_t socket_buffer_size=0;
+ socklen_t socket_buffer_size_len = sizeof(socket_buffer_size);
+ socklen_t send_buffer_size;
+ socklen_t recv_buffer_size;
+
+ size_t bytes_allowed_to_send=(max_send_bytes==(size_t)-1)?send_buffer.size():max_send_bytes;
+ size_t bytes_allowed_to_recv=(max_recv_bytes==(size_t)-1)?recv_buffer.avail():max_recv_bytes;
+
+ if (bytes_sent) *bytes_sent=0;
+ if (bytes_rcvd) *bytes_rcvd=0;
+
+ switch (m_state)
+ {
+ case STATE_RESOLVING:
+ if (saddr==0)
+ {
+ int a=m_dns->resolve(m_host, m_remote_port, &saddr, SOCK_STREAM);
+ if (!a)
+ {
+ m_state=STATE_RESOLVED;
+ }
+ else if (a == 1)
+ {
+ m_state=STATE_RESOLVING;
+ break;
+ }
+ else
+ {
+ m_errorstr="resolving hostname";
+ m_state=STATE_ERROR;
+
+ return;
+ }
+ }
+ // fall through
+ case STATE_RESOLVED:
+ m_socket=::socket(saddr->ai_family, saddr->ai_socktype, saddr->ai_protocol);
+ if (m_socket==-1)
+ {
+ m_errorstr="creating socket";
+ m_state=STATE_ERROR;
+ }
+ else
+ {
+ SET_SOCK_BLOCK(m_socket,0);
+ }
+
+ socket_buffer_size=0;
+ socket_buffer_size_len = sizeof(socket_buffer_size);
+ getsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&socket_buffer_size, &socket_buffer_size_len);
+ send_buffer_size = (int)(send_buffer.avail()+send_buffer.size());
+ if (send_buffer_size > 65536)
+ send_buffer_size=65536;
+ if (socket_buffer_size < send_buffer_size)
+ setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&send_buffer_size, sizeof(send_buffer_size));
+ getsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&socket_buffer_size, &socket_buffer_size_len);
+
+ getsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&socket_buffer_size, &socket_buffer_size_len);
+ recv_buffer_size = (int)recv_buffer.avail();
+ if (recv_buffer_size > 65536)
+ recv_buffer_size=65536;
+ if (socket_buffer_size < recv_buffer_size)
+ setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buffer_size, sizeof(recv_buffer_size));
+ getsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&socket_buffer_size, &socket_buffer_size_len);
+
+ /*
+ ** Joshua Teitelbaum 1/27/2006
+ ** virtualization for ssl
+ */
+ if(!socket_connect())
+ {
+ address=saddr->ai_addr;
+ m_state=STATE_CONNECTED;
+
+ on_socket_connected();
+ }
+ else if (ERRNO!=JNL_EINPROGRESS)
+ {
+ m_errorstr="Connecting to host";
+ m_state=STATE_ERROR;
+ }
+ else
+ {
+ m_state=STATE_CONNECTING;
+ }
+ break;
+ case STATE_CONNECTING:
+ {
+ fd_set f[3];
+ FD_ZERO(&f[0]);
+ FD_ZERO(&f[1]);
+ FD_ZERO(&f[2]);
+ FD_SET(m_socket,&f[0]);
+ FD_SET(m_socket,&f[1]);
+ FD_SET(m_socket,&f[2]);
+ struct timeval tv;
+ memset(&tv,0,sizeof(tv));
+ if (select((int)m_socket+1,&f[0],&f[1],&f[2],&tv)==-1)
+ {
+ m_errorstr="Connecting to host (calling select())";
+ m_state=STATE_ERROR;
+ }
+ else if (FD_ISSET(m_socket,&f[1]))
+ {
+ m_state=STATE_CONNECTED;
+ on_socket_connected();
+ }
+ else if (FD_ISSET(m_socket,&f[2]))
+ {
+ m_errorstr="Connecting to host";
+ m_state=STATE_ERROR;
+ }
+ }
+ break;
+ case STATE_CONNECTED:
+ case STATE_CLOSING:
+ /* --- send --- */
+ {
+ size_t sent = send_buffer.drain(this, bytes_allowed_to_send);
+ if (bytes_sent)
+ *bytes_sent+=sent;
+
+ if (m_state == STATE_CLOSED)
+ break;
+
+ /* --- receive --- */
+ size_t received = recv_buffer.fill(this, bytes_allowed_to_recv);
+ if (bytes_rcvd)
+ *bytes_rcvd+=received;
+ }
+
+ if (m_state == STATE_CLOSING)
+ {
+ if (send_buffer.empty()) m_state = STATE_CLOSED;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void JNL_Connection::on_socket_connected(void)
+{
+ return;
+}
+
+void JNL_Connection::close(int quick)
+{
+ if (quick || m_state == STATE_RESOLVING || m_state == STATE_CONNECTING)
+ {
+ m_state=STATE_CLOSED;
+ /*
+ ** Joshua Teitelbaum 1/27/2006
+ ** virualization for ssl
+ */
+ socket_shutdown();
+
+ m_socket=-1;
+
+ recv_buffer.clear();
+ send_buffer.clear();
+
+ m_remote_port=0;
+ m_host[0]=0;
+ //memset(&m_saddr,0,sizeof(m_saddr));
+ }
+ else
+ {
+ if (m_state == STATE_CONNECTED)
+ m_state=STATE_CLOSING;
+ }
+}
+
+size_t JNL_Connection::send_bytes_in_queue(void)
+{
+ return send_buffer.size();
+}
+
+size_t JNL_Connection::send_bytes_available(void)
+{
+ return send_buffer.avail();
+}
+
+int JNL_Connection::send(const void *data, size_t length)
+{
+ if (length > send_bytes_available())
+ return -1;
+
+ send_buffer.write(data, length);
+ return 0;
+}
+
+int JNL_Connection::send_string(const char *line)
+{
+ return send(line,strlen(line));
+}
+
+size_t JNL_Connection::recv_bytes_available(void)
+{
+ return recv_buffer.size();
+}
+
+size_t JNL_Connection::peek_bytes(void *data, size_t maxlength)
+{
+ if (data)
+ return recv_buffer.peek(data, maxlength);
+ else
+ return min(maxlength, recv_bytes_available());
+}
+
+size_t JNL_Connection::recv_bytes(void *data, size_t maxlength)
+{
+ if (data)
+ return recv_buffer.read(data, maxlength);
+ else
+ return recv_buffer.advance(maxlength);
+}
+
+int JNL_Connection::recv_lines_available(void)
+{
+ int l = (int)recv_bytes_available();
+ int lcount = 0;
+ int lastch = 0;
+
+ for (int pos = 0; pos < l; pos ++)
+ {
+ char t;
+ if (recv_buffer.at(pos, &t, 1) != 1)
+ return lcount;
+
+ if ((t=='\r' || t=='\n') &&( (lastch != '\r' && lastch != '\n') || lastch==t ))
+ lcount++;
+
+ lastch=t;
+ }
+
+ return lcount;
+}
+
+int JNL_Connection::recv_line(char *line, size_t maxlength)
+{
+ while (maxlength--)
+ {
+ char t;
+ if (recv_buffer.read(&t, 1) == 0)
+ {
+ *line=0;
+ return 0;
+ }
+
+ if (t == '\r' || t == '\n')
+ {
+ char r;
+ if (recv_buffer.peek(&r, 1) != 0)
+ {
+ if ((r == '\r' || r == '\n') && r != t)
+ recv_buffer.advance(1);
+ }
+
+ *line=0;
+ return 0;
+
+ }
+
+ *line++=t;
+ }
+
+ return 1;
+}
+
+unsigned long JNL_Connection::get_interface(void)
+{
+ if (m_socket==-1)
+ return 0;
+
+ struct sockaddr_in sin;
+ memset(&sin,0,sizeof(sin));
+ socklen_t len=sizeof(sin);
+
+ if (::getsockname(m_socket,(struct sockaddr *)&sin,&len))
+ return 0;
+
+ return (unsigned long) sin.sin_addr.s_addr;
+}
+
+unsigned long JNL_Connection::get_remote()
+{
+ // TODO: IPv6
+ if (address)
+ {
+ sockaddr_in *ipv4 = (sockaddr_in *)address;
+ return ipv4->sin_addr.s_addr;
+ }
+
+ return 0;
+
+}
+
+unsigned short JNL_Connection::get_remote_port()
+{
+ return m_remote_port;
+}
+
+/* RingBuffer client function */
+size_t JNL_Connection::Read(void *dest, size_t len)
+{
+ if (!len)
+ return 0;
+
+ int res=(int)socket_recv((char *)dest,len,0);
+
+ if (res == 0 || (res < 0 && ERRNO != JNL_EWOULDBLOCK))
+ {
+ m_state=STATE_CLOSED;
+ return 0;
+ }
+
+ if (res > 0)
+ return res;
+ else
+ return 0;
+}
+
+/* RingBuffer client function */
+size_t JNL_Connection::Write(const void *dest, size_t len)
+{
+ if (!len)
+ return 0;
+
+ int res=(int)socket_send((const char *)dest,len,0);
+
+ if (res==-1 && ERRNO != JNL_EWOULDBLOCK)
+ {
+ return 0;
+ // m_state=STATE_CLOSED;
+ }
+
+ if (res > 0)
+ return res;
+ else
+ return 0;
+}
+
+int JNL_Connection::set_recv_buffer_size(size_t new_buffer_size)
+{
+ return recv_buffer.expand(new_buffer_size);
+}
+
+void JNL_Connection::reuse()
+{
+ if (m_state == STATE_CLOSED)
+ {
+ m_state = STATE_CONNECTED;
+ recv_buffer.clear();
+ }
+}
diff --git a/Src/replicant/jnetlib/connection.h b/Src/replicant/jnetlib/connection.h
new file mode 100644
index 000000000..9d32cab26
--- /dev/null
+++ b/Src/replicant/jnetlib/connection.h
@@ -0,0 +1,168 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: connection.h - JNL TCP connection interface
+** License: see jnetlib.h
+**
+** Usage:
+** 1. Create a JNL_Connection object, optionally specifying a JNL_AsyncDNS
+** object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto),
+** and the send and receive buffer sizes.
+** 2. Call connect() to have it connect to a host/port (the hostname will be
+** resolved if possible).
+** 3. call run() with the maximum send/recv amounts, and optionally parameters
+** so you can tell how much has been send/received. You want to do this a lot, while:
+** 4. check get_state() to check the state of the connection. The states are:
+** JNL_Connection::STATE_ERROR
+** - an error has occured on the connection. the connection has closed,
+** and you can no longer write to the socket (there still might be
+** data in the receive buffer - use recv_bytes_available()).
+** JNL_Connection::STATE_NOCONNECTION
+** - no connection has been made yet. call connect() already! :)
+** JNL_Connection::STATE_RESOLVING
+** - the connection is still waiting for a JNL_AsycnDNS to resolve the
+** host.
+** JNL_Connection::STATE_CONNECTING
+** - the asynchronous call to connect() is still running.
+** JNL_Connection::STATE_CONNECTED
+** - the connection has connected, all is well.
+** JNL_Connection::STATE_CLOSING
+** - the connection is closing. This happens after a call to close,
+** without the quick parameter set. This means that the connection
+** will close once the data in the send buffer is sent (data could
+** still be being received when it would be closed). After it is
+** closed, the state will transition to:
+** JNL_Connection::STATE_CLOSED
+** - the connection has closed, generally without error. There still
+** might be data in the receieve buffer, use recv_bytes_available().
+** 5. Use send() and send_string() to send data. You can use
+** send_bytes_in_queue() to see how much has yet to go out, or
+** send_bytes_available() to see how much you can write. If you use send()
+** or send_string() and not enough room is available, both functions will
+** return error ( < 0)
+** 6. Use recv() and recv_line() to get data. If you want to see how much data
+** there is, use recv_bytes_available() and recv_lines_available(). If you
+** call recv() and not enough data is available, recv() will return how much
+** data was actually read. See comments at the function defs.
+**
+** 7. To close, call close(1) for a quick close, or close() for a close that will
+** make the socket close after sending all the data sent.
+**
+** 8. delete ye' ol' object.
+*/
+
+#ifndef _CONNECTION_H_
+#define _CONNECTION_H_
+
+#include "netinc.h"
+#include "asyncdns.h"
+#include "../nu/RingBuffer.h"
+#include "jnetlib_defines.h"
+#include <stddef.h>
+#include "nswasabi/ReferenceCounted.h"
+
+#if defined(_MSC_VER) && (_MSC_VER < 1200)
+typedef int intptr_t;
+#endif
+
+
+#define PACKET_SIZE 16384
+
+class JNL_Connection : private Filler, private Drainer, public ReferenceCountedBase<JNL_Connection>
+{
+public:
+ typedef enum
+ {
+ STATE_ERROR = JNL_CONNECTION_STATE_ERROR,
+ STATE_NOCONNECTION = JNL_CONNECTION_STATE_NOCONNECTION,
+ STATE_RESOLVING = JNL_CONNECTION_STATE_RESOLVING,
+ STATE_CONNECTING = JNL_CONNECTION_STATE_CONNECTING,
+ STATE_CONNECTED = JNL_CONNECTION_STATE_CONNECTED,
+ STATE_CLOSING = JNL_CONNECTION_STATE_CLOSING,
+ STATE_CLOSED = JNL_CONNECTION_STATE_CLOSED,
+ STATE_RESOLVED = JNL_CONNECTION_STATE_RESOLVED,
+ } state;
+
+ /*
+ ** Joshua Teitelbaum, 1/27/2006 adding virtual
+ */
+ JNL_Connection();
+ JNL_Connection(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize);
+ virtual ~JNL_Connection();
+
+ void open( JNL_AsyncDNS *dns = JNL_AUTODNS, size_t sendbufsize = 8192, size_t recvbufsize = 8192 );
+ void connect( const char *hostname, int port );
+ virtual void connect( SOCKET sock, sockaddr *addr, socklen_t length /* of addr */ ); // used by the listen object, usually not needed by users.
+
+ int set_recv_buffer_size(size_t new_buffer_size);
+ /*
+ ** Joshua Teitelbaum 2/2/2006
+ ** Need to make this virtual to ensure SSL can init properly
+ */
+ virtual void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL );
+
+ int get_state() { return m_state; }
+ char *get_errstr() { return m_errorstr; }
+
+ void close( int quick = 0 );
+ void flush_send( void ) { send_buffer.clear(); }
+
+ size_t send_bytes_in_queue( void );
+ size_t send_bytes_available( void );
+ int send( const void *data, size_t length ); // returns -1 if not enough room
+ inline int send_bytes( const void *data, size_t length ) { return send( data, length ); }
+ int send_string( const char *line ); // returns -1 if not enough room
+
+ size_t recv_bytes_available( void );
+ size_t recv_bytes( void *data, size_t maxlength ); // returns actual bytes read
+ unsigned int recv_int( void );
+ int recv_lines_available( void );
+ int recv_line( char *line, size_t maxlength ); // returns 0 if the line was terminated with a \r or \n, 1 if not.
+ // (i.e. if you specify maxlength=10, and the line is 12 bytes long
+ // it will return 1. or if there is no \r or \n and that's all the data
+ // the connection has.)
+ size_t peek_bytes( void *data, size_t maxlength ); // returns bytes peeked
+
+ unsigned long get_interface( void ); // this returns the interface the connection is on
+ unsigned long get_remote( void ); // remote host ip.
+ unsigned short get_remote_port( void ); // this returns the remote port of connection
+
+ void set_dns( JNL_AsyncDNS *dns );
+ void reuse();
+
+protected:
+ SOCKET m_socket;
+ unsigned short m_remote_port;
+
+ RingBuffer recv_buffer;
+ RingBuffer send_buffer;
+
+ addrinfo *saddr;
+ sockaddr *address;
+
+ char m_host[256];
+
+ JNL_AsyncDNS *m_dns;
+ bool m_dns_owned;
+
+ state m_state;
+ char *m_errorstr;
+
+ /*
+ ** Joshua Teitelbaum 1/27/2006 Adding new BSD socket analogues for SSL compatibility
+ */
+ virtual void socket_shutdown();
+ virtual ssize_t socket_recv( char *buf, size_t len, int options );
+ virtual ssize_t socket_send( const char *buf, size_t len, int options );
+ virtual int socket_connect();
+ virtual void on_socket_connected();
+
+private:
+ void init(); // constructor helper function
+
+ // functions for RingBuffer
+ size_t Read( void *dest, size_t len ) override;
+ size_t Write( const void *dest, size_t len ) override;
+};
+#endif // _Connection_H_
diff --git a/Src/replicant/jnetlib/headers.cpp b/Src/replicant/jnetlib/headers.cpp
new file mode 100644
index 000000000..7e702cd27
--- /dev/null
+++ b/Src/replicant/jnetlib/headers.cpp
@@ -0,0 +1,119 @@
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#include "headers.h"
+#include "netinc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+JNL_Headers::JNL_Headers()
+{
+ m_recvheaders = NULL;
+ m_recvheaders_size = 0;
+}
+
+JNL_Headers::~JNL_Headers()
+{
+ if ( m_recvheaders )
+ free( m_recvheaders );
+}
+
+void JNL_Headers::Reset()
+{
+ if ( m_recvheaders )
+ free( m_recvheaders );
+
+ m_recvheaders = NULL;
+ m_recvheaders_size = 0;
+}
+
+const char *JNL_Headers::GetAllHeaders()
+{
+ // double null terminated, null delimited list
+ if ( m_recvheaders )
+ return m_recvheaders;
+ else
+ return "\0\0";
+}
+
+const char *JNL_Headers::GetHeader( const char *headername )
+{
+ char *ret = NULL;
+
+ if ( headername[ 0 ] == 0 || !m_recvheaders )
+ return NULL;
+
+ size_t headername_size = strlen( headername );
+ char *buf = (char *)malloc( headername_size + 2 );
+ strcpy( buf, headername );
+
+ if ( buf[ headername_size - 1 ] != ':' )
+ {
+ buf[ headername_size++ ] = ':';
+ buf[ headername_size ] = 0;
+ }
+
+ char *p = m_recvheaders;
+ while ( p && *p )
+ {
+ if ( !strncasecmp( buf, p, headername_size ) )
+ {
+ ret = p + headername_size;
+ while ( ret && *ret && *ret == ' ' )
+ ret++;
+
+ break;
+ }
+
+ p += strlen( p ) + 1;
+ }
+
+ free( buf );
+
+ return ret;
+}
+
+int JNL_Headers::Add( const char *buf )
+{
+ if ( !m_recvheaders )
+ {
+ m_recvheaders_size = strlen( buf ) + 1;
+ if ( m_recvheaders_size == 0 || m_recvheaders_size == (size_t)-1 ) // check for overflow
+ {
+ return NErr_OutOfMemory;
+ }
+
+ m_recvheaders = (char *)malloc( m_recvheaders_size + 1 );
+ if ( m_recvheaders )
+ {
+ strcpy( m_recvheaders, buf ); // safe because we malloc'd specifically above
+ m_recvheaders[ m_recvheaders_size ] = 0;
+ }
+ else
+ {
+ return NErr_OutOfMemory;
+ }
+ }
+ else
+ {
+ size_t oldsize = m_recvheaders_size;
+ m_recvheaders_size += strlen( buf ) + 1;
+ if ( m_recvheaders_size + 1 < oldsize ) // check for overflow
+ {
+ return NErr_OutOfMemory;
+ }
+
+ char *n = (char *)realloc( m_recvheaders, m_recvheaders_size + 1 );
+ if ( !n )
+ {
+ return NErr_OutOfMemory;
+ }
+
+ strcpy( n + oldsize, buf ); // safe because we malloc specifially for the size
+ n[ m_recvheaders_size ] = 0; // double null terminate
+ m_recvheaders = n;
+ }
+
+ return NErr_Success;
+}
diff --git a/Src/replicant/jnetlib/headers.h b/Src/replicant/jnetlib/headers.h
new file mode 100644
index 000000000..9412ac74e
--- /dev/null
+++ b/Src/replicant/jnetlib/headers.h
@@ -0,0 +1,20 @@
+#pragma once
+
+
+// TODO: benski> change this to use a smarter data structure.
+// this initial implementation is known to work, however
+class JNL_Headers
+{
+public:
+ JNL_Headers();
+ ~JNL_Headers();
+
+ const char *GetAllHeaders();
+ const char *GetHeader( const char *header_name );
+ int Add( const char *buf );
+ void Reset();
+
+private:
+ char *m_recvheaders;
+ size_t m_recvheaders_size;
+}; \ No newline at end of file
diff --git a/Src/replicant/jnetlib/httpget.cpp b/Src/replicant/jnetlib/httpget.cpp
new file mode 100644
index 000000000..d3a83452e
--- /dev/null
+++ b/Src/replicant/jnetlib/httpget.cpp
@@ -0,0 +1,845 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: httpget.cpp - JNL HTTP GET implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "httpget.h"
+#include "foundation/error.h"
+
+#ifdef USE_SSL
+#include "sslconnection.h"
+#endif
+
+#include <stdio.h>
+
+#define STRSAFE_NO_DEPRECATE
+
+#include "nu/strsafe.h"
+#include "nu/AutoLock.h"
+
+char *JNL_HTTPGet::g_proxy = 0;
+
+static nu::LockGuard proxy_guard;
+
+char *JNL_HTTPGet::get_proxy()
+{
+ nu::AutoLock auto_lock( proxy_guard );
+
+ if ( g_proxy )
+ return _strdup( g_proxy );
+ else
+ return 0;
+}
+
+void JNL_HTTPGet::set_proxy( const char *proxy )
+{
+ nu::AutoLock auto_lock( proxy_guard );
+
+ free( g_proxy );
+
+ if ( proxy )
+ g_proxy = _strdup( proxy );
+ else
+ g_proxy = 0;
+}
+
+JNL_HTTPGet::JNL_HTTPGet( size_t recvbufsize, size_t sendbufsize )
+{
+ persistent = false;
+ accept_all_reply_codes = false;
+ zlibStream = 0;
+ allowCompression = false;
+ m_dns = JNL_AUTODNS;
+ m_con = NULL;
+ m_http_proxylpinfo = 0;
+ m_http_proxyhost = 0;
+ m_http_proxyport = 0;
+ m_sendbufsize = sendbufsize;
+ m_sendheaders = NULL;
+
+ reinit();
+
+ m_recvbufsize = recvbufsize;
+
+ char *p = get_proxy();
+ if ( p )
+ {
+ char *r = NULL;
+ do_parse_url( p, &m_http_proxyhost, &m_http_proxyport, &r, &m_http_proxylpinfo );
+
+ free( r );
+ free( p );
+ }
+}
+
+JNL_HTTPGet::~JNL_HTTPGet()
+{
+ deinit();
+ free( m_sendheaders );
+ free( m_http_proxylpinfo );
+ free( m_http_proxyhost );
+}
+
+void JNL_HTTPGet::reinit()
+{
+ m_errstr = 0;
+ m_recvheaders = NULL;
+ m_recvheaders_size = 0;
+ m_http_state = 0;
+ m_http_port = 0;
+ m_http_url = 0;
+ m_reply = 0;
+ m_http_host = m_http_lpinfo = m_http_request = NULL;
+}
+
+void JNL_HTTPGet::deinit( bool full )
+{
+ if ( !persistent || full || ( m_con && m_con->get_state() == JNL_Connection::STATE_ERROR ) )
+ {
+ delete m_con;
+ m_con = NULL;
+ }
+
+ free( m_recvheaders );
+ free( m_http_url );
+ free( m_http_host );
+ free( m_http_lpinfo );
+ free( m_http_request );
+ free( m_errstr );
+ free( m_reply );
+
+ if ( zlibStream )
+ inflateEnd( zlibStream );
+
+ free( zlibStream );
+ zlibStream = 0;
+
+ reinit();
+}
+
+void JNL_HTTPGet::set_sendbufsize(size_t sendbufsize)
+{
+ m_sendbufsize = sendbufsize;
+}
+
+int JNL_HTTPGet::set_recv_buffer_size( size_t new_buffer_size )
+{
+ if ( m_con )
+ {
+ int ret = m_con->set_recv_buffer_size( new_buffer_size );
+ if ( ret == NErr_NoAction )// this will get returned if new_buffer_size is smaller than existing.
+ return NErr_Success;
+ else if ( ret != NErr_Success )
+ return ret;
+ }
+
+ m_recvbufsize = new_buffer_size;
+ return NErr_Success;
+}
+
+void JNL_HTTPGet::addheader( const char *header )
+{
+ if ( strstr( header, "\r" ) || strstr( header, "\n" ) )
+ return;
+
+ if ( !m_sendheaders )
+ {
+ size_t len = strlen( header ) + 3;
+ m_sendheaders = (char *)malloc( len );
+ if ( m_sendheaders )
+ {
+ char *itr = m_sendheaders;
+ StringCchCopyExA( itr, len, header, &itr, &len, 0 );
+ StringCchCatExA( itr, len, "\r\n", &itr, &len, 0 );
+ }
+ }
+ else
+ {
+ size_t len = strlen( header ) + strlen( m_sendheaders ) + 1 + 2;
+ char *t = (char *)malloc( len );
+ if ( t )
+ {
+ char *newHeaders = t;
+ StringCchCopyExA( t, len, m_sendheaders, &t, &len, 0 );
+ StringCchCatExA( t, len, header, &t, &len, 0 );
+ StringCchCatExA( t, len, "\r\n", &t, &len, 0 );
+ free( m_sendheaders );
+ m_sendheaders = newHeaders;
+ }
+ }
+}
+
+void JNL_HTTPGet::addheadervalue( const char *header, const char *value )
+{
+ size_t additional = strlen( header ) + 2 + strlen( value ) + 2 + 1;
+
+ if ( !m_sendheaders )
+ {
+ m_sendheaders = (char *)malloc( additional );
+ if ( m_sendheaders )
+ {
+ char *p = m_sendheaders;
+ StringCchCopyExA( p, additional, header, &p, &additional, 0 );
+ StringCchCatExA( p, additional, ": ", &p, &additional, 0 );
+ StringCchCatExA( p, additional, value, &p, &additional, 0 );
+ StringCchCatExA( p, additional, "\r\n", &p, &additional, 0 );
+ }
+ }
+ else
+ {
+ size_t alloc_len = strlen( m_sendheaders ) + additional;
+ char *t = (char *)malloc( alloc_len );
+ if ( t )
+ {
+ char *p = t;
+ StringCchCopyExA( p, alloc_len, m_sendheaders, &p, &alloc_len, 0 );
+ StringCchCatExA( p, alloc_len, header, &p, &alloc_len, 0 );
+ StringCchCatExA( p, alloc_len, ": ", &p, &alloc_len, 0 );
+ StringCchCatExA( p, alloc_len, value, &p, &alloc_len, 0 );
+ StringCchCatExA( p, alloc_len, "\r\n", &p, &alloc_len, 0 );
+
+ free( m_sendheaders );
+
+ m_sendheaders = t;
+ }
+ }
+}
+
+void JNL_HTTPGet::do_encode_mimestr( char *in, char *out )
+{
+ char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int shift = 0;
+ int accum = 0;
+
+ while ( in && *in )
+ {
+ if ( *in )
+ {
+ accum <<= 8;
+ shift += 8;
+ accum |= *in++;
+ }
+
+ while ( shift >= 6 )
+ {
+ shift -= 6;
+ *out++ = alphabet[ ( accum >> shift ) & 0x3F ];
+ }
+ }
+
+ if ( shift == 4 )
+ {
+ *out++ = alphabet[ ( accum & 0xF ) << 2 ];
+ *out++ = '=';
+ }
+ else if ( shift == 2 )
+ {
+ *out++ = alphabet[ ( accum & 0x3 ) << 4 ];
+ *out++ = '=';
+ *out++ = '=';
+ }
+
+ *out++ = 0;
+}
+
+
+void JNL_HTTPGet::connect( const char *url, int ver, const char *requestmethod )
+{
+ deinit( false );
+
+ m_http_url = _strdup( url );
+ do_parse_url( m_http_url, &m_http_host, &m_http_port, &m_http_request, &m_http_lpinfo );
+
+ if ( !m_http_host || !m_http_host[ 0 ] || !m_http_port )
+ {
+ m_http_state = -1;
+ seterrstr( "invalid URL" );
+
+ return;
+ }
+
+ size_t sendbufferlen = 0;
+
+ if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] )
+ sendbufferlen += strlen( requestmethod ) + 1 /* GET */ + strlen( m_http_request ) + 9 /* HTTP/1.0 */ + 2;
+ else
+ {
+ sendbufferlen += strlen( requestmethod ) + 1 /* GET */ + strlen( m_http_url ) + 9 /* HTTP/1.0 */ + 2;
+
+ if ( m_http_proxylpinfo && m_http_proxylpinfo[ 0 ] )
+ sendbufferlen += 58 + strlen( m_http_proxylpinfo ) * 2; // being safe here
+ }
+
+ sendbufferlen += 5 /* Host: */ + strlen( m_http_host ) + 2;
+ if ( m_http_port != 80 )
+ sendbufferlen += 6;
+
+ if ( m_http_lpinfo && m_http_lpinfo[ 0 ] )
+ sendbufferlen += 46 + strlen( m_http_lpinfo ) * 2; // being safe here
+
+ if ( m_sendheaders )
+ sendbufferlen += strlen( m_sendheaders );
+
+ size_t strLen = sendbufferlen + 1024;
+ char *str = (char *)calloc( strLen, sizeof( char ) );
+ char *connectString = str;
+
+ if ( !str )
+ {
+ seterrstr( "error allocating memory" );
+ m_http_state = -1;
+ }
+
+ if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] )
+ {
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s %s HTTP/1.%d\r\n", requestmethod, m_http_request, ver % 10 );
+ }
+ else
+ {
+ char *myp = NULL;
+ if ( strncasecmp( m_http_url, "uvox://", 7 ) == 0 )
+ {
+ myp = m_http_url + 7;
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 );
+ }
+ else if ( strncasecmp( m_http_url, "unsv://", 7 ) == 0 )
+ {
+ myp = m_http_url + 7;
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 );
+ }
+ else if ( strncasecmp( m_http_url, "uasf://", 7 ) == 0 )
+ {
+ myp = m_http_url + 7;
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 );
+ }
+ else
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s %s HTTP/1.%d\r\n", requestmethod, m_http_url, ver % 10 );
+ }
+
+ if ( m_http_port == 80 )
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "Host: %s\r\n", m_http_host );
+ else
+ StringCchPrintfExA( str, strLen, &str, &strLen, 0, "Host: %s:%d\r\n", m_http_host, m_http_port );
+
+ if ( m_http_lpinfo && m_http_lpinfo[ 0 ] )
+ {
+ StringCchCatExA( str, strLen, "Authorization: Basic ", &str, &strLen, 0 );
+ do_encode_mimestr( m_http_lpinfo, str );
+ StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 );
+ }
+
+ if ( m_http_proxylpinfo && m_http_proxylpinfo[ 0 ] )
+ {
+ StringCchCatExA( str, strLen, "Proxy-Authorization: Basic ", &str, &strLen, 0 );
+ do_encode_mimestr( m_http_proxylpinfo, str );
+ StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 );
+ }
+
+ if ( allowCompression )
+ StringCchCatExA( str, strLen, "Accept-Encoding: gzip\r\n", &str, &strLen, 0 );
+
+ if ( m_sendheaders )
+ StringCchCatExA( str, strLen, m_sendheaders, &str, &strLen, 0 );
+
+ StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 );
+
+ int a = (int)m_recvbufsize;
+ if ( a < 4096 )
+ a = 4096;
+
+ if ( !m_con )
+ {
+ //m_con=new JNL_Connection(m_dns,strlen(str)+4,a);
+ /*
+ ** Joshua Teitelbaum delta 1/15/2006
+ */
+
+#ifdef USE_SSL
+ /*
+ ** Joshua Teitelbaum 1/27/2006
+ ** Check for secure
+ */
+ if ( !_strnicmp( m_http_url, "https:", strlen( "https:" ) ) )
+ {
+ size_t send_buffer_size = strlen( connectString ) + 4;
+ if ( send_buffer_size < 8192 )
+ send_buffer_size = 8192;
+
+ send_buffer_size += m_sendbufsize;
+
+ if ( m_sendbufsize == 0 && !strcmp( requestmethod, "POST" ) )
+ send_buffer_size += 8192; // some extra room for posting data if it wasn't explicitly defined
+
+ m_con = new JNL_SSL_Connection( NULL, m_dns, send_buffer_size, a );
+ }
+ else
+ {
+#endif
+
+ size_t send_buffer_size = strlen( connectString ) + 4 + m_sendbufsize;
+ if ( m_sendbufsize == 0 && !strcmp( requestmethod, "POST" ) )
+ send_buffer_size += 8192; // some extra room for posting data if it wasn't explicitly defined
+
+ m_con = new JNL_Connection( m_dns, send_buffer_size, a );
+
+#ifdef USE_SSL
+ }
+#endif
+
+ if ( m_con )
+ {
+ if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] )
+ m_con->connect( m_http_host, m_http_port );
+ else
+ m_con->connect( m_http_proxyhost, m_http_proxyport );
+
+ m_con->send_string( connectString );
+ }
+ else
+ {
+ m_http_state = -1;
+ seterrstr( "could not create connection object" );
+ }
+ }
+ else
+ {
+ m_con->reuse();
+ m_con->send_string( connectString );
+ }
+
+ free(connectString);
+}
+
+void JNL_HTTPGet::do_parse_url( const char *url, char **host, unsigned short *port, char **req, char **lp )
+{
+ char *l_port = 0;
+
+ JNL::parse_url( url, &l_port, host, port, req, lp );
+
+ if ( !*port )
+ {
+ if ( l_port )
+ {
+ addrinfo *res;
+
+ addrinfo hints;
+ memset( &hints, 0, sizeof( hints ) );
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = 0;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ( getaddrinfo( 0, l_port, &hints, &res ) == 0 )
+ {
+ if ( res->ai_family == AF_INET )
+ *port = htons( ( (sockaddr_in *)res->ai_addr )->sin_port );
+ else if ( res->ai_family == AF_INET6 )
+ *port = htons( ( (sockaddr_in6 *)res->ai_addr )->sin6_port );
+ else // wtf?
+ *port = 80;
+ }
+ else
+ *port = 80;
+ }
+ else
+ *port = 80;
+ }
+
+ if ( l_port )
+ free( l_port );
+
+ if ( !*req )
+ *req = _strdup( "/" );
+}
+
+const char *JNL_HTTPGet::getallheaders()
+{
+ // double null terminated, null delimited list
+ if (m_recvheaders)
+ return m_recvheaders;
+ else
+ return "\0\0";
+}
+
+const char *JNL_HTTPGet::getheader( const char *headername )
+{
+ char *ret = NULL;
+ if ( headername[ 0 ] == 0 || !m_recvheaders )
+ return NULL;
+
+ size_t headername_size = strlen( headername );
+ char *buf = (char *)malloc( headername_size + 2 );
+
+#ifdef _WIN32
+ StringCchCopyA( buf, headername_size + 2, headername );
+#elif defined(__APPLE__)
+ strlcpy( buf, headername, headername_size + 2 );
+#else
+ strncpy( buf, headername, headername_size + 1 );
+ buf[ headername_size + 1 ] = 0;
+#endif
+
+ if ( buf[ headername_size - 1 ] != ':' )
+ {
+ buf[ headername_size++ ] = ':';
+ buf[ headername_size ] = 0;
+ }
+
+ char *p = m_recvheaders;
+ while ( p && *p )
+ {
+ if ( !strncasecmp( buf, p, headername_size ) )
+ {
+ ret = p + headername_size;
+ while ( ret && *ret && *ret == ' ' )
+ ret++;
+
+ break;
+ }
+
+ p += strlen( p ) + 1;
+ }
+
+ free( buf );
+
+ return ret;
+}
+
+int JNL_HTTPGet::run()
+{
+ int cnt = 0;
+ if ( m_http_state == -1 || !m_con )
+ return HTTPGET_RUN_ERROR; // error
+
+
+run_again:
+ m_con->run();
+
+ if ( m_con->get_state() == JNL_Connection::STATE_ERROR )
+ {
+ seterrstr( m_con->get_errstr() );
+
+ return HTTPGET_RUN_ERROR;
+ }
+
+ if ( m_con->get_state() == JNL_Connection::STATE_CLOSED )
+ return HTTPGET_RUN_CONNECTION_CLOSED;
+
+ if ( m_http_state == 0 ) // connected, waiting for reply
+ {
+ if ( m_con->recv_lines_available() > 0 )
+ {
+ char buf[ 4096 ] = { 0 };
+ m_con->recv_line( buf, 4096 );
+ buf[ 4095 ] = 0;
+
+ if ( m_reply && getreplycode() == 100 )
+ {
+ free( m_reply );
+ m_reply = 0;
+ goto run_again;
+ }
+
+ m_reply = _strdup( buf );
+ int code = getreplycode();
+ if ( code >= 200 && code <= 206 )
+ m_http_state = 2; // proceed to read headers normally
+ else if ( code == 301 || code == 302 || code == 303 || code == 307 )
+ {
+ m_http_state = 1; // redirect city
+ }
+ else if ( code != 100 ) // in case of HTTP 100 Continue code, we'll keep looping
+ {
+ if ( accept_all_reply_codes )
+ {
+ m_http_state = 2; // proceed to read headers normally
+ }
+ else
+ {
+ seterrstr( buf );
+ m_http_state = -1;
+
+ return HTTPGET_RUN_ERROR;
+ }
+ }
+
+ cnt = 0;
+ }
+ else if ( !cnt++ )
+ goto run_again;
+ }
+
+ if ( m_http_state == 1 ) // redirect
+ {
+ char *loc = 0;
+ while ( m_con->recv_lines_available() > 0 )
+ {
+ char buf[ 4096 ] = { 0 };
+ m_con->recv_line( buf, 4096 );
+ buf[ 4095 ] = 0;
+
+ if ( !buf[ 0 ] )
+ {
+ if ( !loc )
+ {
+ m_http_state = -1;
+ return HTTPGET_RUN_ERROR;
+ }
+ else
+ break;
+ }
+
+ if ( !strncasecmp( buf, "Location:", 9 ) )
+ {
+ char *p = buf + 9;
+ while ( p && *p && *p == ' ' ) p++;
+ if ( p && *p )
+ {
+ // TODO need to make this match the request type
+ loc = _strdup( p );
+ }
+ }
+ }
+
+ if ( loc )
+ {
+ connect( loc );
+ free( loc );
+
+ return HTTPGET_RUN_OK;
+ }
+ }
+
+ /* ----- read headers ----- */
+ if ( m_http_state == 2 )
+ {
+ if ( !cnt++ && m_con->recv_lines_available() < 1 )
+ goto run_again;
+
+ while ( m_con->recv_lines_available() > 0 )
+ {
+ char buf[ 8192 ] = { 0 };
+ m_con->recv_line( buf, 8192 );
+ buf[ 8191 ] = 0;
+
+ if ( !buf[ 0 ] )
+ {
+ const char *compression = getheader( "Content-Encoding" );
+ if ( compression && !strcmp( compression, "gzip" ) )
+ {
+ zlibStream = (z_stream *)malloc( sizeof( z_stream ) );
+ zlibStream->next_in = Z_NULL;
+ zlibStream->avail_in = Z_NULL;
+ zlibStream->next_out = Z_NULL;
+ zlibStream->avail_out = Z_NULL;
+ zlibStream->zalloc = (alloc_func)0;
+ zlibStream->zfree = (free_func)0;
+ zlibStream->opaque = 0;
+
+ int z_err = inflateInit2( zlibStream, 15 + 16 /* +16 for gzip */ );
+ if ( z_err != Z_OK )
+ {
+ free( zlibStream );
+ zlibStream = 0;
+ }
+ }
+ else
+ {
+ if ( zlibStream )
+ {
+ free( zlibStream );
+ zlibStream = 0;
+ }
+ }
+ m_http_state = 3;
+ break;
+ }
+ if ( !m_recvheaders )
+ {
+ m_recvheaders_size = strlen( buf ) + 1;
+ if ( m_recvheaders_size == 0 || m_recvheaders_size == (size_t)-1 ) // check for overflow
+ {
+ m_http_state = -1;
+ return HTTPGET_RUN_ERROR;
+ }
+
+ m_recvheaders = (char *)malloc( m_recvheaders_size + 1 );
+ if ( m_recvheaders )
+ {
+ strcpy( m_recvheaders, buf ); // safe because we malloc'd specifically above
+ m_recvheaders[ m_recvheaders_size ] = 0;
+ }
+ else
+ {
+ m_http_state = -1;
+ return HTTPGET_RUN_ERROR;
+ }
+ }
+ else
+ {
+ size_t oldsize = m_recvheaders_size;
+ m_recvheaders_size += strlen( buf ) + 1;
+ if ( m_recvheaders_size + 1 < oldsize ) // check for overflow
+ {
+ m_http_state = -1;
+ return HTTPGET_RUN_ERROR;
+ }
+
+ char *n = (char *)realloc( m_recvheaders, m_recvheaders_size + 1 );
+ if ( !n )
+ {
+ m_http_state = -1;
+ return HTTPGET_RUN_ERROR;
+ }
+
+ strcpy( n + oldsize, buf ); // safe because we malloc specifially for the size
+ n[ m_recvheaders_size ] = 0; // double null terminate
+ m_recvheaders = n;
+ }
+ }
+ }
+
+ return HTTPGET_RUN_OK;
+}
+
+int JNL_HTTPGet::get_status() // returns 0 if connecting, 1 if reading headers, 2 if reading content, -1 if error.
+{
+ if ( m_http_state < 0 )
+ return HTTPGET_STATUS_ERROR;
+
+ if ( m_http_state < 2 )
+ return HTTPGET_STATUS_CONNECTING;
+
+ if ( m_http_state == 2 )
+ return HTTPGET_STATUS_READING_HEADERS;
+
+ if ( m_http_state == 3 )
+ return HTTPGET_STATUS_READING_CONTENT;
+
+ return HTTPGET_STATUS_ERROR;
+}
+
+int JNL_HTTPGet::getreplycode() // returns 0 if none yet, otherwise returns http reply code.
+{
+ if ( !m_reply )
+ return 0;
+
+ char *p = m_reply;
+
+ while ( p && *p && *p != ' ' )
+ p++; // skip over HTTP/x.x
+
+ if ( !p || !*p )
+ return 0;
+
+ return atoi( ++p );
+}
+
+size_t JNL_HTTPGet::bytes_available()
+{
+ if ( m_con && m_http_state == 3 )
+ return m_con->recv_bytes_available();
+
+ return 0;
+}
+
+size_t JNL_HTTPGet::get_bytes( char *buf, size_t len )
+{
+ if ( m_con && m_http_state == 3 )
+ {
+ if ( zlibStream )
+ {
+ // TODO: benski> we need to pick a better buffer size
+ // either alloca() and use the passed in length
+ // or malloc a buffer based on the constructor-initted buffer size
+ char temp[ 8192 ] = { 0 };
+
+ int size = (int)m_con->peek_bytes( temp, 8192 );
+ if ( size )
+ {
+ zlibStream->next_in = reinterpret_cast<Bytef *>( temp );
+ zlibStream->avail_in = (uInt)size;
+ zlibStream->next_out = reinterpret_cast<Bytef *>( buf );
+ zlibStream->avail_out = (uInt)len;
+
+ int zlib_err = inflate( zlibStream, Z_SYNC_FLUSH );
+
+ if ( zlib_err == Z_OK || zlib_err == Z_STREAM_END )
+ {
+ m_con->recv_bytes( 0, size - zlibStream->avail_in ); // since we only peeked above
+ return len - zlibStream->avail_out;
+ }
+ else
+ return 0; // TODO: should we do something else here?
+ }
+ }
+ else
+ return m_con->recv_bytes( buf, len );
+ }
+
+ return 0;
+}
+
+size_t JNL_HTTPGet::peek_bytes( char *buf, size_t len )
+{
+ if ( m_con && m_http_state == 3 )
+ {
+ if ( zlibStream )
+ return 0; // TODO: benski> how are we going to do peek_bytes, since the inflater saves state?
+ else
+ return m_con->peek_bytes( buf, len );
+ }
+
+ return 0;
+}
+
+uint64_t JNL_HTTPGet::content_length()
+{
+ const char *p = getheader( "content-length" );
+ if ( p && *p )
+ return strtoull( p, 0, 10 );
+ else
+ {
+ // TODO need to check this further for reliability!
+ // Helps to handle responses without content-length
+ if ( m_recvheaders_size > 0 && bytes_available() > 0 )
+ return bytes_available() - m_recvheaders_size;
+ }
+ return 0;
+}
+
+void JNL_HTTPGet::seterrstr( const char *str )
+{
+ if ( m_errstr )
+ free( m_errstr );
+
+ m_errstr = _strdup( str );
+}
+
+void JNL_HTTPGet::AllowCompression()
+{
+ allowCompression = true;
+}
+
+void JNL_HTTPGet::reset_headers()
+{
+ if ( m_sendheaders )
+ {
+ free( m_sendheaders );
+ m_sendheaders = 0;
+ }
+}
+
+void JNL_HTTPGet::set_accept_all_reply_codes()
+{
+ accept_all_reply_codes = true;
+}
+
+void JNL_HTTPGet::set_persistent()
+{
+ persistent = true;
+}
diff --git a/Src/replicant/jnetlib/httpget.h b/Src/replicant/jnetlib/httpget.h
new file mode 100644
index 000000000..b29a17ced
--- /dev/null
+++ b/Src/replicant/jnetlib/httpget.h
@@ -0,0 +1,139 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: httpget.h - JNL interface for doing HTTP GETs.
+** License: see jnetlib.h
+**
+** Usage:
+** 1. Create a JNL_HTTPGet object, optionally specifying a JNL_AsyncDNS
+** object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto),
+** and the receive buffer size, and a string specifying proxy (or NULL
+** for none). See note on proxy string below.
+** 2. call addheader() to add whatever headers you want. It is recommended to
+** add at least the following two:
+** addheader("User-Agent:MyApp (Mozilla)");
+*/// addheader("Accept:*/*");
+/* ( the comment weirdness is there so I Can do the star-slash :)
+** 3. Call connect() with the URL you wish to GET (see URL string note below)
+** 4. Call run() once in a while, checking to see if it returns -1
+** (if it does return -1, call geterrorstr() to see what the error is).
+** (if it returns 1, no big deal, the connection has closed).
+** 5. While you're at it, you can call bytes_available() to see if any data
+** from the http stream is available, or getheader() to see if any headers
+** are available, or getreply() to see the HTTP reply, or getallheaders()
+** to get a double null terminated, null delimited list of headers returned.
+** 6. If you want to read from the stream, call get_bytes (which returns how much
+** was actually read).
+** 7. content_length() is a helper function that uses getheader() to check the
+** content-length header.
+** 8. Delete ye' ol' object when done.
+**
+** Proxy String:
+** should be in the format of host:port, or user@host:port, or
+** user:password@host:port. if port is not specified, 80 is assumed.
+** URL String:
+** should be in the format of http://user:pass@host:port/requestwhatever
+** note that user, pass, port, and /requestwhatever are all optional :)
+** note that also, http:// is really not important. if you do poo://
+** or even leave out the http:// altogether, it will still work.
+*/
+
+#ifndef _HTTPGET_H_
+#define _HTTPGET_H_
+
+#include "connection.h"
+#include "asyncdns.h"
+#include "jnetlib_defines.h"
+#ifdef _WIN32
+#include "minizip/unzip.h"
+#else
+#include <zlib.h>
+#endif
+#include "nswasabi/ReferenceCounted.h"
+
+class JNL_HTTPGet : public ReferenceCountedBase<JNL_HTTPGet>
+{
+public:
+ JNL_HTTPGet(size_t recvbufsize=PACKET_SIZE, size_t sendbufsize=0);
+ ~JNL_HTTPGet();
+
+ void set_sendbufsize( size_t sendbufsize = PACKET_SIZE ); // call if you're going to POST or do any kind of bidirectional communications
+ int set_recv_buffer_size(size_t new_buffer_size);
+ void addheader(const char *header);
+ void addheadervalue(const char *header, const char *value);
+
+ void connect(const char *url, int ver=0, const char *requestmethod="GET");
+
+ int run(); // returns: 0 if all is OK. -1 if error (call geterrorstr()). 1 if connection closed.
+
+ int get_status(); // returns 0 if connecting, 1 if reading headers,
+ // 2 if reading content, -1 if error.
+
+ const char *getallheaders(); // double null terminated, null delimited list
+ const char *getheader(const char *headername);
+ const char *getreply() { return m_reply; }
+ int getreplycode(); // returns 0 if none yet, otherwise returns http reply code.
+
+ const char *geterrorstr() { return m_errstr;}
+
+ size_t bytes_available();
+ size_t get_bytes(char *buf, size_t len);
+ size_t peek_bytes(char *buf, size_t len);
+
+ uint64_t content_length();
+
+ JNL_Connection *get_con() { return m_con; }
+ void AllowCompression();
+ void reset_headers();
+
+ void set_dns(JNL_AsyncDNS *dns);
+ const char *get_url() { return m_http_url; }
+ void set_accept_all_reply_codes(); // call this if you want to retrieve content even though a 404 (etc) was returned
+ void set_persistent();
+ static void set_proxy(const char *proxy);
+
+protected:
+ static char *get_proxy();
+ void reinit();
+ void deinit(bool full=true);
+ void seterrstr(const char *str);
+
+ void do_parse_url(const char *url, char **host, unsigned short*port, char **req, char **lp);
+ void do_encode_mimestr(char *in, char *out);
+
+ JNL_AsyncDNS *m_dns;
+ JNL_Connection *m_con;
+ size_t m_recvbufsize;
+
+ int m_http_state;
+
+ unsigned short m_http_port;
+ char *m_http_url;
+ char *m_http_host;
+ char *m_http_lpinfo;
+ char *m_http_request;
+
+ char *m_http_proxylpinfo;
+ char *m_http_proxyhost;
+ unsigned short m_http_proxyport;
+
+ char *m_sendheaders;
+ char *m_recvheaders;
+ size_t m_recvheaders_size;
+ char *m_reply;
+
+ char *m_errstr;
+ bool allowCompression;
+
+ size_t m_sendbufsize;
+ /* gzip stuff */
+ z_stream *zlibStream;
+
+ bool accept_all_reply_codes;
+ bool persistent;
+
+ static char *g_proxy;
+};
+
+#endif // _HTTPGET_H_
diff --git a/Src/replicant/jnetlib/httpserv.cpp b/Src/replicant/jnetlib/httpserv.cpp
new file mode 100644
index 000000000..4a129e9b3
--- /dev/null
+++ b/Src/replicant/jnetlib/httpserv.cpp
@@ -0,0 +1,234 @@
+/*
+** JNetLib
+** Copyright (C) 2001 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: httpserv.cpp - JNL HTTP GET/POST serving implementation
+** License: see jnetlib.h
+**
+** This class just manages the http reply/sending, not where the data
+** comes from, etc.
+*/
+
+#include "netinc.h"
+#include "util.h"
+
+#include "httpserv.h"
+
+/*
+ States for m_state:
+ -1 error (connection closed, etc)
+ 0 not read request yet.
+ 1 reading headers
+ 2 headers read, have not sent reply
+ 3 sent reply
+ 4 closed
+*/
+
+JNL_HTTPServ::JNL_HTTPServ(JNL_Connection *con)
+{
+ m_con=con;
+ m_state=0;
+ m_reply_headers=0;
+ m_reply_string=0;
+ m_recv_request=0;
+ m_errstr=0;
+ m_reply_ready=0;
+ m_method = 0;
+ http_ver = 0;
+ keep_alive = 0;
+}
+
+JNL_HTTPServ::~JNL_HTTPServ()
+{
+ free(m_recv_request);
+ free(m_reply_string);
+ free(m_reply_headers);
+ free(m_errstr);
+ free(m_method);
+ m_con->Release();
+}
+
+static size_t strlen_whitespace(const char *str)
+{
+ size_t size=0;
+ while (str && *str && *str != ' ' && *str != '\r' && *str!='\n')
+ {
+ str++;
+ size++;
+ }
+ return size;
+}
+
+int JNL_HTTPServ::run()
+{ // returns: < 0 on error, 0 on connection close, 1 if reading request, 2 if reply not sent, 3 if reply sent, sending data.
+ int cnt=0;
+run_again:
+ m_con->run();
+ if (m_con->get_state()==JNL_Connection::STATE_ERROR)
+ {
+ seterrstr(m_con->get_errstr());
+ return -1;
+ }
+
+ if (m_con->get_state()==JNL_Connection::STATE_CLOSED)
+ return 4;
+
+ if (m_state == 0)
+ {
+ if (m_con->recv_lines_available()>0)
+ {
+ char *buf=(char*)malloc(m_con->recv_bytes_available()-1);
+ m_con->recv_line(buf,m_con->recv_bytes_available()-1);
+ free(m_recv_request);
+ m_recv_request=(char*)malloc(strlen(buf)+2);
+ strcpy(m_recv_request,buf);
+ m_recv_request[strlen(m_recv_request)+1]=0;
+ free(buf);
+ buf=m_recv_request;
+ while (buf && *buf) buf++;
+ while (buf >= m_recv_request && *buf != ' ') buf--;
+ if (strncmp(buf+1,"HTTP",4))// || strncmp(m_recv_request,"GET ",3))
+ {
+ seterrstr("malformed HTTP request");
+ m_state=-1;
+ }
+ else
+ {
+ http_ver = atoi(buf+8);
+
+ size_t method_len = strlen_whitespace(m_recv_request);
+ m_method = (char *)malloc(method_len + 1);
+ memcpy(m_method, m_recv_request, method_len);
+ m_method[method_len]=0;
+
+ m_state=1;
+ cnt=0;
+ if (buf >= m_recv_request) buf[0]=buf[1]=0;
+
+ buf=strstr(m_recv_request,"?");
+ if (buf)
+ {
+ *buf++=0; // change &'s into 0s now.
+ char *t=buf;
+ int stat=1;
+ while (t && *t)
+ {
+ if (*t == '&' && !stat) { stat=1; *t=0; }
+ else stat=0;
+ t++;
+ }
+ }
+ }
+ }
+ else if (!cnt++) goto run_again;
+ }
+ if (m_state == 1)
+ {
+ if (!cnt++ && m_con->recv_lines_available()<1) goto run_again;
+ while (m_con->recv_lines_available()>0)
+ {
+ char buf[4096] = {0};
+ m_con->recv_line(buf,4096);
+ if (!buf[0])
+ {
+ m_state=2;
+ break;
+ }
+ recvheaders.Add(buf);
+ }
+ }
+ if (m_state == 2)
+ {
+ if (m_reply_ready)
+ {
+ // send reply
+ m_con->send_string((char*)(m_reply_string?m_reply_string:"HTTP/1.1 200 OK"));
+ m_con->send_string("\r\n");
+ if (m_reply_headers) m_con->send_string(m_reply_headers);
+ m_con->send_string("\r\n");
+ m_state=3;
+ }
+ }
+ if (m_state == 3)
+ {
+ // nothing.
+ }
+
+ return m_state;
+}
+
+const char *JNL_HTTPServ::get_request_file()
+{
+ // file portion of http request
+ if (!m_recv_request) return NULL;
+ char *t=m_recv_request;
+ while (t && *t && *t != ' ') t++;
+ if (!t || !*t) return NULL;
+ while (t && *t && *t == ' ') t++;
+ return t;
+}
+
+const char *JNL_HTTPServ::get_request_parm(const char *parmname) // parameter portion (after ?)
+{
+ const char *t=m_recv_request;
+ while (t && *t) t++;
+ if (t) t++;
+ while (t && *t)
+ {
+ while (t && *t && *t == '&') t++;
+ if (!_strnicmp(t,parmname,strlen(parmname)) && t[strlen(parmname)] == '=')
+ {
+ return t+strlen(parmname)+1;
+ }
+ t+=strlen(t)+1;
+ }
+ return NULL;
+}
+
+const char *JNL_HTTPServ::getheader(const char *headername)
+{
+ return recvheaders.GetHeader(headername);
+}
+
+void JNL_HTTPServ::set_reply_string(const char *reply_string) // should be HTTP/1.1 OK or the like
+{
+ free(m_reply_string);
+ m_reply_string=(char*)malloc(strlen(reply_string)+1);
+ strcpy(m_reply_string,reply_string);
+}
+
+void JNL_HTTPServ::add_reply_header(const char *header) // "Connection: close" for example
+{
+ // if they've specified a content-length, then we can keep alive an HTTP/1.1 connection
+ if (!keep_alive && http_ver == 1 && !_strnicmp(header, "Content-Length", 14))
+ keep_alive = 1;
+
+ if (m_reply_headers)
+ {
+ char *tmp=(char*)malloc(strlen(m_reply_headers)+strlen(header)+3);
+ strcpy(tmp,m_reply_headers);
+ strcat(tmp,header);
+ strcat(tmp,"\r\n");
+ free(m_reply_headers);
+ m_reply_headers=tmp;
+ }
+ else
+ {
+ m_reply_headers=(char*)malloc(strlen(header)+3);
+ strcpy(m_reply_headers,header);
+ strcat(m_reply_headers,"\r\n");
+ }
+}
+
+void JNL_HTTPServ::reset()
+{
+ free(m_recv_request); m_recv_request = 0;
+ free(m_reply_string); m_reply_string = 0;
+ free(m_reply_headers); m_reply_headers = 0;
+ free(m_errstr); m_errstr = 0;
+ free(m_method); m_method =0;
+ m_reply_ready=0;
+ m_state = 0;
+ keep_alive = 0;
+}
+
diff --git a/Src/replicant/jnetlib/httpserv.h b/Src/replicant/jnetlib/httpserv.h
new file mode 100644
index 000000000..3b8e8cb5f
--- /dev/null
+++ b/Src/replicant/jnetlib/httpserv.h
@@ -0,0 +1,71 @@
+/*
+** JNetLib
+** Copyright (C) 2001 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: httpserv.h - JNL interface for doing HTTP GET/POST serving.
+** License: see jnetlib.h
+** This class just manages the http reply/sending, not where the data
+** comes from, etc.
+** for a mini-web server see webserver.h
+*/
+
+#ifndef _HTTPSERV_H_
+#define _HTTPSERV_H_
+
+#include "connection.h"
+#include "headers.h"
+#include "nswasabi/ReferenceCounted.h"
+
+class JNL_HTTPServ : public ReferenceCountedBase<JNL_HTTPServ>
+{
+ public:
+ JNL_HTTPServ(JNL_Connection *con=NULL);
+ ~JNL_HTTPServ();
+
+ int run(); // returns: < 0 on error, 0 on request not read yet, 1 if reading headers, 2 if reply not sent, 3 if reply sent, sending data. 4 on connection closed.
+
+ const char *geterrorstr() { return m_errstr;}
+
+ // use these when state returned by run() is 2
+ const char *get_request_file(); // file portion of http request
+ const char *get_request_parm(const char *parmname); // parameter portion (after ?)
+ const char *getallheaders() { return recvheaders.GetAllHeaders(); } // double null terminated, null delimited list
+ const char *getheader(const char *headername);
+ const char *get_method() { return m_method; };
+ void set_reply_string(const char *reply_string); // should be HTTP/1.1 OK or the like
+ void add_reply_header(const char *header); // i.e. "content-size: 12345"
+
+ void send_reply() { m_reply_ready=1; } // send reply, state will advance to 3.
+
+ ////////// sending data ///////////////
+ int bytes_inqueue() { if (m_state == 3 || m_state == -1 || m_state ==4) return (int)m_con->send_bytes_in_queue(); else return 0; }
+ int bytes_cansend() { if (m_state == 3) return (int)m_con->send_bytes_available(); else return 0; }
+ void write_bytes(char *bytes, int length) { m_con->send(bytes,length); }
+
+ void close(int quick) { m_con->close(quick); m_state=4; }
+
+ JNL_Connection *get_con() { return m_con; }
+
+ void reset(); // prepare for another request on the same connection (HTTP/1.1)
+ int get_http_version() { return http_ver; }
+ int get_keep_alive() { return keep_alive; }
+
+ protected:
+ void seterrstr(const char *str) { if (m_errstr) free(m_errstr); m_errstr=_strdup(str); }
+
+ int m_reply_ready;
+ int m_state;
+ int http_ver;
+ int keep_alive;
+
+ char *m_errstr;
+ char *m_reply_headers;
+ char *m_reply_string;
+ JNL_Headers recvheaders;
+ char *m_recv_request; // either double-null terminated, or may contain parameters after first null.
+ char *m_method;
+
+ JNL_Connection *m_con;
+};
+
+#endif // _HTTPSERV_H_
diff --git a/Src/replicant/jnetlib/httpuserv.cpp b/Src/replicant/jnetlib/httpuserv.cpp
new file mode 100644
index 000000000..de14219ab
--- /dev/null
+++ b/Src/replicant/jnetlib/httpuserv.cpp
@@ -0,0 +1,216 @@
+/*
+** JNetLib
+** Copyright (C) 2012 Nullsoft, Inc.
+** Author: Ben Allison
+** File: httpuserv.cpp - JNL HTTPU (HTTP over UDP) serving implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "httpuserv.h"
+
+#include "foundation/error.h"
+
+/*
+States for m_state:
+-1 error (connection closed, etc)
+0 not read request yet.
+1 reading headers
+2 headers read, have not sent reply
+3 sent reply
+4 closed
+*/
+
+JNL_HTTPUServ::JNL_HTTPUServ()
+{
+ m_reply_headers=0;
+ m_reply_string=0;
+ m_recv_request=0;
+ m_errstr=0;
+ m_reply_ready=0;
+ m_method = 0;
+ http_ver = 0;
+}
+
+JNL_HTTPUServ::~JNL_HTTPUServ()
+{
+ free(m_recv_request);
+ free(m_reply_string);
+ free(m_reply_headers);
+ free(m_errstr);
+ free(m_method);
+}
+
+static size_t strlen_whitespace(const char *str)
+{
+ size_t size=0;
+ while (str && *str && *str != ' ' && *str != '\r' && *str!='\n')
+ {
+ str++;
+ size++;
+ }
+ return size;
+}
+
+int JNL_HTTPUServ::process(JNL_UDPConnection *m_con)
+{ // returns: < 0 on error, 0 on connection close, 1 if reading request, 2 if reply not sent, 3 if reply sent, sending data.
+
+ reset();
+ if (m_con->get_state()==JNL_CONNECTION_STATE_ERROR)
+ {
+ seterrstr(m_con->get_errstr());
+ return -1;
+ }
+
+ if (m_con->get_state()==JNL_CONNECTION_STATE_CLOSED)
+ return 4;
+
+ if (m_con->recv_lines_available()>0)
+ {
+ char *buf=(char*)malloc(m_con->recv_bytes_available()-1);
+ m_con->recv_line(buf,m_con->recv_bytes_available()-1);
+ free(m_recv_request);
+ m_recv_request=(char*)malloc(strlen(buf)+2);
+ strcpy(m_recv_request,buf);
+ m_recv_request[strlen(m_recv_request)+1]=0;
+ free(buf);
+ buf=m_recv_request;
+ while (buf && *buf) buf++;
+ while (buf >= m_recv_request && *buf != ' ') buf--;
+ if (strncmp(buf+1,"HTTP",4))// || strncmp(m_recv_request,"GET ",3))
+ {
+ seterrstr("malformed HTTP request");
+ }
+ else
+ {
+ http_ver = atoi(buf+8);
+
+ size_t method_len = strlen_whitespace(m_recv_request);
+ m_method = (char *)malloc(method_len + 1);
+ if (m_method)
+ {
+ memcpy(m_method, m_recv_request, method_len);
+ m_method[method_len]=0;
+ }
+ else
+ {
+ seterrstr("malformed HTTP request");
+ return -1;
+ }
+
+ if (buf >= m_recv_request) buf[0]=buf[1]=0;
+
+ buf=strstr(m_recv_request,"?");
+ if (buf)
+ {
+ *buf++=0; // change &'s into 0s now.
+ char *t=buf;
+ int stat=1;
+ while (t && *t)
+ {
+ if (*t == '&' && !stat) { stat=1; *t=0; }
+ else stat=0;
+ t++;
+ }
+ }
+ }
+ }
+ else
+ {
+ seterrstr("malformed HTTP request");
+ return -1;
+ }
+
+ while (m_con->recv_lines_available()>0)
+ {
+ char buf[8192] = {0};
+ m_con->recv_line(buf, 8192);
+ if (!buf[0])
+ break;
+
+ recvheaders.Add(buf);
+ }
+
+
+ return NErr_Success;
+}
+
+void JNL_HTTPUServ::send_reply(JNL_UDPConnection *m_con)
+{
+ m_con->send_string((char*)(m_reply_string?m_reply_string:"HTTP/1.1 200 OK"));
+ m_con->send_string("\r\n");
+ if (m_reply_headers) m_con->send_string(m_reply_headers);
+ m_con->send_string("\r\n");
+
+}
+
+const char *JNL_HTTPUServ::get_request_uri()
+{
+ // file portion of http request
+ if (!m_recv_request) return NULL;
+ char *t=m_recv_request;
+ while (t && *t && *t != ' ') t++;
+ if (!t || !*t) return NULL;
+ while (t && *t && *t == ' ') t++;
+ return t;
+}
+
+const char *JNL_HTTPUServ::get_request_parm(const char *parmname) // parameter portion (after ?)
+{
+ const char *t=m_recv_request;
+ while (*t) t++;
+ t++;
+ while (t && *t)
+ {
+ while (t && *t && *t == '&') t++;
+ if (!strncasecmp(t,parmname,strlen(parmname)) && t[strlen(parmname)] == '=')
+ {
+ return t+strlen(parmname)+1;
+ }
+ t+=strlen(t)+1;
+ }
+ return NULL;
+}
+
+const char *JNL_HTTPUServ::getheader(const char *headername)
+{
+ return recvheaders.GetHeader(headername);
+}
+
+void JNL_HTTPUServ::set_reply_string(const char *reply_string) // should be HTTP/1.1 OK or the like
+{
+ free(m_reply_string);
+ m_reply_string=(char*)malloc(strlen(reply_string)+1);
+ strcpy(m_reply_string,reply_string);
+}
+
+void JNL_HTTPUServ::set_reply_header(const char *header) // "Connection: close" for example
+{
+ if (m_reply_headers)
+ {
+ char *tmp=(char*)malloc(strlen(m_reply_headers)+strlen(header)+3);
+ strcpy(tmp,m_reply_headers);
+ strcat(tmp,header);
+ strcat(tmp,"\r\n");
+ free(m_reply_headers);
+ m_reply_headers=tmp;
+ }
+ else
+ {
+ m_reply_headers=(char*)malloc(strlen(header)+3);
+ strcpy(m_reply_headers,header);
+ strcat(m_reply_headers,"\r\n");
+ }
+}
+
+void JNL_HTTPUServ::reset()
+{
+ free(m_recv_request); m_recv_request = 0;
+ free(m_reply_string); m_reply_string = 0;
+ free(m_reply_headers); m_reply_headers = 0;
+ free(m_errstr); m_errstr = 0;
+ free(m_method); m_method =0;
+ recvheaders.Reset();
+ m_reply_ready=0;
+}
diff --git a/Src/replicant/jnetlib/httpuserv.h b/Src/replicant/jnetlib/httpuserv.h
new file mode 100644
index 000000000..d6015067e
--- /dev/null
+++ b/Src/replicant/jnetlib/httpuserv.h
@@ -0,0 +1,55 @@
+/*
+** JNetLib
+** Copyright (C) 2012 Nullsoft, Inc.
+** Author: Ben Allison
+** File: httpuserv.h - JNL interface for doing HTTPU (HTTP over UDP)
+** This is half-baked so far. Need to think things through a touch more
+*/
+
+#pragma once
+
+#include "udpconnection.h"
+#include "headers.h"
+
+class JNL_HTTPUServ
+{
+public:
+ JNL_HTTPUServ();
+ ~JNL_HTTPUServ();
+
+ // pass this a connection that has just received a packet
+ int process( JNL_UDPConnection *m_con );
+
+ const char *geterrorstr() { return m_errstr; }
+
+ // use these when state returned by run() is 2
+ const char *get_request_uri(); // file portion of http request
+ const char *get_request_parm( const char *parmname ); // parameter portion (after ?)
+ const char *getallheaders() { return recvheaders.GetAllHeaders(); } // double null terminated, null delimited list
+ const char *getheader( const char *headername );
+ const char *get_method() { return m_method; }
+
+ void set_reply_string( const char *reply_string ); // should be HTTP/1.1 OK or the like
+ void set_reply_header( const char *header ); // i.e. "content-size: 12345"
+
+ void send_reply( JNL_UDPConnection *m_con ); // sends a reply to the given UDP socket. it must have been setup beforehand with the appropriate peer
+
+ void reset(); // prepare for another request
+
+ int get_http_version() { return http_ver; }
+
+protected:
+ void seterrstr( const char *str ) { if ( m_errstr ) free( m_errstr ); m_errstr = _strdup( str ); }
+
+ int m_reply_ready;
+ int http_ver;
+
+ char *m_errstr;
+ char *m_reply_headers;
+ char *m_reply_string;
+ JNL_Headers recvheaders;
+ char *m_recv_request; // either double-null terminated, or may contain parameters after first null.
+ char *m_method;
+};
+
+
diff --git a/Src/replicant/jnetlib/jnetlib-replicant.rc b/Src/replicant/jnetlib/jnetlib-replicant.rc
new file mode 100644
index 000000000..fcff77115
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib-replicant.rc
@@ -0,0 +1,76 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/replicant/jnetlib/jnetlib-replicant.vcproj b/Src/replicant/jnetlib/jnetlib-replicant.vcproj
new file mode 100644
index 000000000..a514c3c0d
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib-replicant.vcproj
@@ -0,0 +1,491 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="jnetlib-replicant"
+ ProjectGUID="{AAE8BF3C-AD6B-405C-A216-BF62E2D49E95}"
+ RootNamespace="jnetlib"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;../../openssl/include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4995;4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll"
+ LinkIncremental="2"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="msvcprt.lib"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)\$(TargetName).pdb"
+ SubSystem="2"
+ ImportLibrary="$(IntDir)\$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="..;../../openssl/include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL;WIN32_LEAN_AND_MEAN"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4995;4996;4244"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll"
+ LinkIncremental="1"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="msvcprt.lib"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(IntDir)\$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(PlatformName)\$(ConfigurationName)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;../../openssl/include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4995;4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll"
+ LinkIncremental="2"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="msvcprt.lib"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)\$(TargetName).pdb"
+ SubSystem="2"
+ ImportLibrary="$(IntDir)\$(TargetName).lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="..;../../openssl/include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;USE_SSL;WIN32_LEAN_AND_MEAN"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4995;4996;4244"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ OutputFile="$(ProgramFiles)\Winamp\Shared\jnetlib.dll"
+ LinkIncremental="1"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="msvcprt.lib"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(IntDir)\$(TargetName).lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ <ProjectReference
+ ReferencedProjectIdentifier="{EFC75A79-269F-44FC-BAC5-D7D4FD4EC92C}"
+ RelativePathToProject="..\replicant\nu\nu.vcproj"
+ />
+ <ProjectReference
+ ReferencedProjectIdentifier="{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
+ RelativePathToProject="..\replicant\zlib\zlib.vcproj"
+ />
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\asyncdns.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\connection.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\headers.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\httpget.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\httpserv.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\httpuserv.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\jnetlib.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\listen.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\multicastlisten.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sslconnection.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\udpconnection.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\util.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\asyncdns.h"
+ >
+ </File>
+ <File
+ RelativePath=".\connection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\headers.h"
+ >
+ </File>
+ <File
+ RelativePath=".\httpget.h"
+ >
+ </File>
+ <File
+ RelativePath=".\httpserv.h"
+ >
+ </File>
+ <File
+ RelativePath=".\httpuserv.h"
+ >
+ </File>
+ <File
+ RelativePath=".\jnetlib.h"
+ >
+ </File>
+ <File
+ RelativePath=".\listen.h"
+ >
+ </File>
+ <File
+ RelativePath=".\multicastlisten.h"
+ >
+ </File>
+ <File
+ RelativePath=".\netinc.h"
+ >
+ </File>
+ <File
+ RelativePath=".\precomp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sslconnection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\udpconnection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\util.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ >
+ <File
+ RelativePath=".\jnetlib-replicant.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/Src/replicant/jnetlib/jnetlib.cpp b/Src/replicant/jnetlib/jnetlib.cpp
new file mode 100644
index 000000000..00fcfbdba
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib.cpp
@@ -0,0 +1,630 @@
+#include "jnetlib.h"
+#include "httpget.h"
+#include "sslconnection.h"
+#include "asyncdns.h"
+#include "util.h"
+#include "httpserv.h"
+#include "httpuserv.h"
+#include "listen.h"
+#include "multicastlisten.h"
+
+#include "foundation/error.h"
+
+#include <new>
+
+int jnl_init()
+{
+ return JNL::open_socketlib();
+}
+
+void jnl_quit()
+{
+ JNL::close_socketlib();
+}
+
+/* --- Connection --- */
+jnl_connection_t jnl_connection_create(jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize)
+{
+ JNL_Connection *connection = new (std::nothrow) JNL_Connection((JNL_AsyncDNS *)dns, sendbufsize, recvbufsize);
+ return (jnl_connection_t)connection;
+}
+
+jnl_connection_t jnl_sslconnection_create(jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize)
+{
+ JNL_SSL_Connection *connection = new (std::nothrow) JNL_SSL_Connection(NULL, (JNL_AsyncDNS *)dns, sendbufsize, recvbufsize);
+ return (jnl_connection_t)connection;
+}
+
+void jnl_connection_run(jnl_connection_t _connection, size_t max_send_bytes, size_t max_receive_bytes, size_t *bytes_sent, size_t *bytes_received)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ connection->run(max_send_bytes, max_receive_bytes, bytes_sent, bytes_received);
+}
+
+int jnl_connection_get_state(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->get_state();
+}
+
+size_t jnl_connection_send_bytes_available(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->send_bytes_available();
+}
+
+size_t jnl_connection_receive_bytes_available(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->recv_bytes_available();
+}
+
+int jnl_connection_send(jnl_connection_t _connection, const void *bytes, size_t size)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->send(bytes, size);
+}
+
+JNL_API int jnl_connection_send_string(jnl_connection_t _connection, const char *str)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->send_string(str);
+}
+
+size_t jnl_connection_send_bytes_in_queue(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->send_bytes_in_queue();
+}
+
+size_t jnl_connection_receive(jnl_connection_t _connection, void *bytes, size_t size)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->recv_bytes(bytes, size);
+}
+
+size_t jnl_connection_peek(jnl_connection_t _connection, void *bytes, size_t size)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->peek_bytes(bytes, size);
+}
+
+void jnl_connection_release(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ if (connection)
+ connection->Release();
+}
+
+int jnl_connection_receive_line(jnl_connection_t _connection, void *bytes, size_t size)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->recv_line((char *)bytes, size);
+}
+
+size_t jnl_connection_receive_lines_available(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->recv_lines_available();
+}
+
+void jnl_connection_close(jnl_connection_t _connection, int fast)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ connection->close(fast);
+}
+
+void jnl_connection_connect(jnl_connection_t _connection, const char *hostname, int port)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ connection->connect(hostname, port);
+}
+
+const char *jnl_connection_get_error(jnl_connection_t _connection)
+{
+ JNL_Connection *connection = (JNL_Connection *)_connection;
+ return connection->get_errstr();
+}
+
+/* ---- UDP ----- */
+int jnl_udp_create_multicast_listener(jnl_udp_t *connection, const char *mcast_ip, unsigned short port)
+{
+ JNL_UDPConnection *udp = 0;
+ JNL::open_socketlib(); // TODO: cut
+
+ int ret = CreateMulticastListener(&udp, mcast_ip, port);
+ if (ret != NErr_Success)
+ {
+ JNL::close_socketlib(); // TODO: cut
+
+ return ret;
+ }
+ *connection = (jnl_udp_t)udp;
+ return NErr_Success;
+}
+
+void jnl_udp_release(jnl_udp_t _connection)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ delete connection; // TODO: reference count
+ JNL::close_socketlib(); // TODO: cut
+}
+
+void jnl_udp_run(jnl_udp_t _connection, size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ if (connection)
+ {
+ connection->run(max_send_bytes, max_recv_bytes, bytes_sent, bytes_rcvd);
+ }
+}
+
+size_t jnl_udp_recv_bytes(jnl_udp_t _connection, void *buf, size_t len)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ if (connection)
+ {
+ return connection->recv_bytes(buf, len);
+ }
+ else
+ return 0;
+}
+
+int jnl_udp_send(jnl_udp_t _connection, const void *bytes, size_t size)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ return connection->send(bytes, size);
+}
+
+void jnl_udp_set_peer(jnl_udp_t _connection, const char *hostname, unsigned short port)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ connection->setpeer(hostname, port);
+}
+
+void jnl_udp_set_peer_address(jnl_udp_t _connection, sockaddr *addr, socklen_t length)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ connection->setpeer(addr, length);
+}
+
+int jnl_udp_get_address(jnl_udp_t _connection, sockaddr **addr, socklen_t *length)
+{
+ JNL_UDPConnection *connection = (JNL_UDPConnection *)_connection;
+ connection->get_last_recv_msg_addr(addr, length);
+ return NErr_Success;
+}
+
+/* ---- HTTP ---- */
+jnl_http_t jnl_http_create(int recvbufsize, int sendbufsize)
+{
+ JNL_HTTPGet *http = new (std::nothrow) JNL_HTTPGet(recvbufsize, sendbufsize);
+ return (jnl_http_t)http;
+}
+
+int jnl_http_set_recv_buffer_size(jnl_http_t _http, size_t new_size)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->set_recv_buffer_size(new_size);
+}
+
+int jnl_http_run(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->run();
+}
+
+size_t jnl_http_get_bytes(jnl_http_t _http, void *buf, size_t len)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->get_bytes(static_cast<char *>(buf), len);
+}
+
+size_t jnl_http_peek_bytes(jnl_http_t _http, void *buf, size_t len)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->peek_bytes(static_cast<char *>(buf), len);
+}
+
+size_t jnl_http_bytes_available(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->bytes_available();
+}
+
+uint64_t jnl_http_content_length(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->content_length();
+}
+
+int jnl_http_get_status(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->get_status();
+}
+
+int jnl_http_getreplycode(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->getreplycode();
+}
+
+const char *jnl_http_getreply(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->getreply();
+}
+
+const char *jnl_http_getheader(jnl_http_t _http, const char *header)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->getheader(header);
+}
+
+const char *jnl_http_get_all_headers(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->getallheaders();
+}
+
+void jnl_http_addheader(jnl_http_t _http, const char *header)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->addheader(header);
+}
+
+void jnl_http_addheadervalue(jnl_http_t _http, const char *header, const char *value)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->addheadervalue(header, value);
+}
+
+void jnl_http_connect(jnl_http_t _http, const char *url, int http_version, const char *method)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->connect(url, http_version, method);
+}
+
+void jnl_http_release(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+
+ if (http)
+ http->Release();
+}
+
+jnl_http_t jnl_http_retain(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ if (http)
+ http->Retain();
+ return _http;
+}
+
+const char *jnl_http_get_url(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return http->get_url();
+}
+
+void jnl_http_reset_headers(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->reset_headers();
+}
+
+void jnl_http_set_persistent(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->set_persistent();
+}
+
+void jnl_http_allow_compression(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->AllowCompression();
+}
+
+void jnl_http_allow_accept_all_reply_codes(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ http->set_accept_all_reply_codes();
+}
+
+jnl_connection_t jnl_http_get_connection(jnl_http_t _http)
+{
+ JNL_HTTPGet *http = (JNL_HTTPGet *)_http;
+ return (jnl_connection_t)http->get_con();
+}
+
+void jnl_http_set_proxy(const char *proxy)
+{
+ JNL_HTTPGet::set_proxy(proxy);
+}
+
+/* ------- HTTP Request Parser ------- */
+int jnl_http_request_create(jnl_http_request_t *_http, jnl_connection_t _connection)
+{
+ JNL_HTTPServ *http = new (std::nothrow) JNL_HTTPServ((JNL_Connection *)_connection);;
+ if (!http)
+ return NErr_OutOfMemory;
+
+ *_http = (jnl_http_request_t) http;
+ return NErr_Success;
+}
+
+void jnl_http_request_release(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ if (http)
+ http->Release();
+}
+
+int jnl_http_request_run(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ return http->run();
+}
+
+int jnl_htt_request_get_keep_alive(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ return http->get_keep_alive();
+}
+
+const char *jnl_http_request_get_header(jnl_http_request_t _http, const char *header)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ return http->getheader(header);
+}
+
+void jnl_http_request_reset(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ http->reset();
+}
+
+void jnl_http_request_addheader(jnl_http_request_t _http, const char *header)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ http->add_reply_header(header);
+}
+
+void jnl_http_request_set_reply_string(jnl_http_request_t _http, const char *reply)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ http->set_reply_string(reply);
+}
+
+void jnl_http_request_send_reply(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ http->send_reply();
+}
+
+const char *jnl_http_request_get_uri(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ return http->get_request_file();
+}
+
+const char *jnl_http_request_get_parameter(jnl_http_request_t _http, const char *parameter)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ return http->get_request_parm(parameter);
+}
+
+jnl_connection_t jnl_http_request_get_connection(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ JNL_Connection *connection = http->get_con();
+ if (connection)
+ {
+ connection->Retain();
+ return (jnl_connection_t)connection;
+ }
+ else
+ return 0;
+}
+
+const char *jnl_http_request_get_method(jnl_http_request_t _http)
+{
+ JNL_HTTPServ *http = (JNL_HTTPServ *)_http;
+ return http->get_method();
+}
+
+/* ------- HTTPU Request Parser ------- */
+int jnl_httpu_request_create(jnl_httpu_request_t *_httpu)
+{
+ JNL_HTTPUServ *httpu = new (std::nothrow) JNL_HTTPUServ;
+ if (!httpu)
+ return NErr_OutOfMemory;
+
+ *_httpu = (jnl_httpu_request_t) httpu;
+ return NErr_Success;
+}
+
+void jnl_httpu_request_release(jnl_httpu_request_t _httpu)
+{
+ JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu;
+ delete httpu; // TODO: reference count
+}
+
+int jnl_httpu_request_process(jnl_httpu_request_t _httpu, jnl_udp_t _udp)
+{
+ JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu;
+ return httpu->process((JNL_UDPConnection *)_udp);
+}
+
+const char *jnl_httpu_request_get_method(jnl_httpu_request_t _httpu)
+{
+ JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu;
+ return httpu->get_method();
+}
+
+
+const char *jnl_httpu_request_get_uri(jnl_httpu_request_t _httpu)
+{
+ JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu;
+ return httpu->get_request_uri();
+}
+
+const char *jnl_httpu_request_get_header(jnl_httpu_request_t _httpu, const char *header)
+{
+ JNL_HTTPUServ *httpu = (JNL_HTTPUServ *)_httpu;
+ return httpu->getheader(header);
+}
+
+/* ----- DNS ----- */
+int jnl_dns_create(jnl_dns_t *out_dns)
+{
+ JNL_AsyncDNS *dns = new (std::nothrow)JNL_AsyncDNS;
+ if (!dns)
+ return NErr_OutOfMemory;
+
+ *out_dns = (jnl_dns_t)dns;
+ return NErr_Success;
+}
+
+void jnl_dns_release(jnl_dns_t _dns)
+{
+ JNL_AsyncDNS *dns = (JNL_AsyncDNS *)_dns;
+ delete dns; // TODO: reference counting
+}
+
+int jnl_dns_resolve(jnl_dns_t _dns, const char *hostname, unsigned short port, addrinfo **addr, int sockettype)
+{
+ JNL_AsyncDNS *dns = (JNL_AsyncDNS *)_dns;
+ int ret = dns->resolve(hostname, port, addr, sockettype);
+ if (ret == 0)
+ return NErr_Success;
+ else if (ret == -1)
+ return NErr_TryAgain;
+ else
+ return NErr_Unknown;
+}
+
+int jnl_dns_resolve_now(const char *hostname, unsigned short port, addrinfo **addr, int sockettype)
+{
+ int ret = JNL_AsyncDNS::resolvenow(hostname, port, addr, sockettype);
+ if (ret == 0)
+ return NErr_Success;
+ else
+ return NErr_Unknown;
+}
+
+
+void jnl_dns_freeaddrinfo(addrinfo *addr)
+{
+ freeaddrinfo(addr);
+}
+
+void jnl_dns_gethostname(char *name, size_t cch)
+{
+ gethostname(name, (int)cch);
+}
+
+#ifdef _WIN32
+PCSTR WSAAPI inet_ntop_xp(INT af, PVOID src, PSTR dst, size_t cnt)
+{
+ struct sockaddr_in srcaddr;
+
+ memset(&srcaddr, 0, sizeof(struct sockaddr_in));
+ memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
+
+ srcaddr.sin_family = af;
+ if (WSAAddressToStringA((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt) != 0) {
+ return NULL;
+ }
+ return dst;
+}
+
+PCSTR WSAAPI inet_ntop_win32(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize) {
+ typedef PCSTR (WSAAPI * win32_inet_ntop)(INT, PVOID, PSTR, size_t);
+ static win32_inet_ntop pwin32_inet_ntop = NULL;
+
+ if (!pwin32_inet_ntop){
+ HMODULE hlib = LoadLibrary(L"WS2_32.DLL");
+ pwin32_inet_ntop = (win32_inet_ntop)GetProcAddress(hlib, "inet_ntop");
+ if (!pwin32_inet_ntop) {
+ pwin32_inet_ntop = inet_ntop_xp;
+ }
+ }
+
+ return (*pwin32_inet_ntop)(Family, pAddr, pStringBuf, StringBufSize);
+}
+
+#endif
+
+void jnl_dns_ntop(int af, const void *src, char *dst, socklen_t size)
+{
+#ifdef _WIN32
+ // TODO need to revist this at a later date
+ // [22:01:08] audiodsp: i will make a tweak for IPv6 compatability at some point
+ // [22:01:49] audiodsp: just change references from sockaddr_in to sockaddr_storage
+ // [22:02:24] audiodsp: keep it as is
+ // [22:02:32] audiodsp: we're only using it in IPv4 mode at the moment
+ // [22:02:40] audiodsp: i will fix when we need IPv6 server support
+ // [22:03:58] audiodsp: the memcpy is what makes it non-trivial
+ // [22:04:05] audiodsp: i have to switch on family and do different memcpy's accordingly
+ // [22:04:16] audiodsp: or change the method to require a length
+ // [22:04:19] audiodsp: which makes more sense
+ // [22:04:29] audiodsp: and just not pass it to the linux function
+ // [22:05:08] audiodsp: anyway not important.
+ inet_ntop_win32(af, (PVOID)src, dst, size);
+#else
+ inet_ntop(af, src, dst, size);
+#endif
+}
+/* Listen */
+
+int jnl_listen_create(jnl_listen_t *_listen, unsigned short port)
+{
+ JNL_Listen *l = new (std::nothrow) JNL_Listen();
+ if (!l)
+ return NErr_OutOfMemory;
+ int ret = l->Initialize(port);
+ if (ret != NErr_Success)
+ {
+ delete l;
+ return ret;
+ }
+ *_listen = (jnl_listen_t)l;
+ return NErr_Success;
+}
+
+int jnl_listen_create_from_address(jnl_listen_t *_listen, struct addrinfo *addr, size_t index)
+{
+ JNL_Listen *l = new (std::nothrow) JNL_Listen();
+ if (!l)
+ return NErr_OutOfMemory;
+ int ret = l->Initialize(addr, index);
+ if (ret != NErr_Success)
+ {
+ delete l;
+ return ret;
+ }
+ *_listen = (jnl_listen_t)l;
+ return NErr_Success;
+}
+
+jnl_connection_t jnl_listen_get_connection(jnl_listen_t _listen)
+{
+ JNL_Listen *listen = (JNL_Listen *)_listen;
+ JNL_Connection *connection = listen->get_connect();
+ return (jnl_connection_t)connection;
+}
+
+unsigned short jnl_listen_get_port(jnl_listen_t _listen)
+{
+ JNL_Listen *listen = (JNL_Listen *)_listen;
+ return listen->get_port();
+}
+
+void jnl_listen_release(jnl_listen_t _listen)
+{
+ JNL_Listen *listen = (JNL_Listen *)_listen;
+ if (listen)
+ listen->Release();
+} \ No newline at end of file
diff --git a/Src/replicant/jnetlib/jnetlib.h b/Src/replicant/jnetlib/jnetlib.h
new file mode 100644
index 000000000..2dcf08ca3
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib.h
@@ -0,0 +1,177 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2006 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: jnetlib.h - JNL main include file (not really necessary).
+**
+** For documentation, look at the following files:
+** Generic network initialization: netinc.h
+** DNS: asyncdns.h
+** TCP connections: connection.h
+** HTTP GET connections: httpget.h
+** TCP listen: listen.h
+**
+** license:
+**
+** This software is provided 'as-is', without any express or implied
+** warranty. In no event will the authors be held liable for any damages
+** arising from the use of this software.
+**
+** Permission is granted to anyone to use this software for any purpose,
+** including commercial applications, and to alter it and redistribute it
+** freely, subject to the following restrictions:
+**
+** 1. The origin of this software must not be misrepresented; you must not
+** claim that you wrote the original software. If you use this software
+** in a product, an acknowledgment in the product documentation would be
+** appreciated but is not required.
+** 2. Altered source versions must be plainly marked as such, and must not be
+** misrepresented as being the original software.
+** 3. This notice may not be removed or altered from any source distribution.
+**
+*/
+
+#ifndef _JNETLIB_H_
+#define _JNETLIB_H_
+
+#include "netinc.h"
+#include "../foundation/types.h"
+#include "jnetlib_defines.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MSC_VER
+#ifdef JNETLIB_EXPORTS
+#define JNL_API __declspec(dllexport)
+#else
+#define JNL_API __declspec(dllimport)
+#endif
+#elif defined(__ANDROID__) || defined(__APPLE__)
+#define JNL_API __attribute__ ((visibility("default")))
+#elif defined(__linux__)
+#if __GNUC__ >= 4
+#define JNL_API __attribute__ ((visibility ("default")))
+#else
+#define JNL_API
+#endif
+#else
+#error port me
+#endif
+
+/* these are reference counted. so make sure to match init/quit calls. */
+JNL_API int jnl_init();
+JNL_API void jnl_quit();
+
+/* ----- Connection ----- */
+JNL_API jnl_connection_t jnl_connection_create( jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize );
+JNL_API jnl_connection_t jnl_sslconnection_create( jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize );
+JNL_API void jnl_connection_run( jnl_connection_t connection, size_t max_send_bytes, size_t max_receive_bytes, size_t *bytes_sent, size_t *bytes_received );
+JNL_API int jnl_connection_get_state( jnl_connection_t connection );
+JNL_API size_t jnl_connection_send_bytes_available( jnl_connection_t connection );
+JNL_API size_t jnl_connection_receive_bytes_available( jnl_connection_t connection );
+JNL_API int jnl_connection_receive_line( jnl_connection_t _connection, void *bytes, size_t size );
+JNL_API int jnl_connection_send( jnl_connection_t connection, const void *bytes, size_t size );
+JNL_API int jnl_connection_send_string( jnl_connection_t connection, const char *str );
+JNL_API size_t jnl_connection_receive( jnl_connection_t connection, void *bytes, size_t size );
+JNL_API size_t jnl_connection_send_bytes_in_queue( jnl_connection_t connection );
+JNL_API void jnl_connection_release( jnl_connection_t connection );
+JNL_API size_t jnl_connection_receive_lines_available( jnl_connection_t connection );
+JNL_API void jnl_connection_close( jnl_connection_t _connection, int fast );
+JNL_API void jnl_connection_connect( jnl_connection_t connection, const char *hostname, int port );
+JNL_API const char *jnl_connection_get_error( jnl_connection_t connection );
+JNL_API size_t jnl_connection_peek( jnl_connection_t _connection, void *bytes, size_t size );
+
+/* ----- UDP ----- */
+JNL_API int jnl_udp_create_multicast_listener( jnl_udp_t *connection, const char *mcast_ip, unsigned short port );
+JNL_API void jnl_udp_release( jnl_udp_t connection );
+JNL_API void jnl_udp_run( jnl_udp_t connection, size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd );
+JNL_API size_t jnl_udp_recv_bytes( jnl_udp_t connection, void *buf, size_t len );
+JNL_API int jnl_udp_send( jnl_udp_t connection, const void *bytes, size_t size );
+JNL_API void jnl_udp_set_peer( jnl_udp_t connection, const char *hostname, unsigned short port );
+JNL_API void jnl_udp_set_peer_address( jnl_udp_t connection, struct sockaddr *addr, socklen_t length );
+// gets the address of whomever sent the last message
+JNL_API int jnl_udp_get_address( jnl_udp_t connection, struct sockaddr **addr, socklen_t *length );
+
+/* ----- HTTP ----- */
+
+/* creation/destruction */
+JNL_API jnl_http_t jnl_http_create( int recvbufsize, int sendbufsize );
+JNL_API int jnl_http_set_recv_buffer_size( jnl_http_t http, size_t new_size ); /* increases the receive buffer size */
+JNL_API jnl_http_t jnl_http_retain( jnl_http_t http );
+JNL_API void jnl_http_release( jnl_http_t http );
+JNL_API jnl_connection_t jnl_http_get_connection( jnl_http_t http );
+/* TODO: replace these with a jnl_http_configure(jnl_http_t http) function */
+JNL_API void jnl_http_set_persistent( jnl_http_t http );
+JNL_API void jnl_http_allow_compression( jnl_http_t http );
+JNL_API void jnl_http_allow_accept_all_reply_codes( jnl_http_t http );
+
+/* run & status stuff */
+JNL_API void jnl_http_connect( jnl_http_t http, const char *url, int http_version, const char *method );
+JNL_API int jnl_http_run( jnl_http_t http );
+JNL_API int jnl_http_get_status( jnl_http_t http );
+JNL_API int jnl_http_getreplycode( jnl_http_t http );
+JNL_API const char *jnl_http_getreply( jnl_http_t http );
+
+/* reading data */
+JNL_API size_t jnl_http_get_bytes( jnl_http_t http, void *buf, size_t len );
+JNL_API size_t jnl_http_peek_bytes( jnl_http_t http, void *buf, size_t len );
+JNL_API size_t jnl_http_bytes_available( jnl_http_t http );
+JNL_API uint64_t jnl_http_content_length( jnl_http_t http );
+
+/* HTTP headers */
+JNL_API const char *jnl_http_getheader( jnl_http_t http, const char *header );
+JNL_API void jnl_http_addheader( jnl_http_t http, const char *header );
+JNL_API void jnl_http_addheadervalue( jnl_http_t http, const char *header, const char *value );
+JNL_API void jnl_http_reset_headers( jnl_http_t http );
+JNL_API const char *jnl_http_get_all_headers( jnl_http_t http );
+
+/* other information */
+JNL_API const char *jnl_http_get_url( jnl_http_t http );
+JNL_API void jnl_http_set_proxy( const char *proxy );
+
+/* ----- HTTP Request Parsing ----- */
+JNL_API int jnl_http_request_create( jnl_http_request_t *http, jnl_connection_t connection );
+JNL_API void jnl_http_request_release( jnl_http_request_t http );
+JNL_API int jnl_http_request_run( jnl_http_request_t http );
+JNL_API int jnl_htt_request_get_keep_alive( jnl_http_request_t http );
+JNL_API const char *jnl_http_request_get_header( jnl_http_request_t http, const char *header );
+JNL_API void jnl_http_request_reset( jnl_http_request_t http );
+JNL_API void jnl_http_request_addheader( jnl_http_request_t http, const char *header );
+JNL_API void jnl_http_request_set_reply_string( jnl_http_request_t http, const char *reply );
+JNL_API void jnl_http_request_send_reply( jnl_http_request_t http );
+JNL_API const char *jnl_http_request_get_uri( jnl_http_request_t http );
+JNL_API const char *jnl_http_request_get_parameter( jnl_http_request_t _http, const char *parameter );
+JNL_API jnl_connection_t jnl_http_request_get_connection( jnl_http_request_t http );
+JNL_API const char *jnl_http_request_get_method( jnl_http_request_t http );
+
+/* ----- HTTPU Request Parsing ----- */
+JNL_API int jnl_httpu_request_create( jnl_httpu_request_t *httpu );
+JNL_API void jnl_httpu_request_release( jnl_httpu_request_t httpu );
+JNL_API int jnl_httpu_request_process( jnl_httpu_request_t httpu, jnl_udp_t udp );
+JNL_API const char *jnl_httpu_request_get_method( jnl_httpu_request_t httpu );
+JNL_API const char *jnl_httpu_request_get_uri( jnl_httpu_request_t httpu );
+JNL_API const char *jnl_httpu_request_get_header( jnl_httpu_request_t httpu, const char *header );
+
+/* ----- DNS ------ */
+JNL_API int jnl_dns_create( jnl_dns_t *dns );
+JNL_API void jnl_dns_release( jnl_dns_t dns );
+JNL_API int jnl_dns_resolve( jnl_dns_t dns, const char *hostname, unsigned short port, struct addrinfo **addr, int sockettype );
+// when you call jnl_dns_resolve_now, you need to call jnl_dns_freeaddrinfo
+JNL_API int jnl_dns_resolve_now( const char *hostname, unsigned short port, struct addrinfo **addr, int sockettype );
+JNL_API void jnl_dns_freeaddrinfo( struct addrinfo *addr );
+JNL_API void jnl_dns_gethostname( char *name, size_t cch );
+JNL_API void jnl_dns_ntop( int af, const void *src, char *dst, socklen_t size );
+/* listen */
+JNL_API int jnl_listen_create( jnl_listen_t *listen, unsigned short port );
+JNL_API int jnl_listen_create_from_address( jnl_listen_t *listen, struct addrinfo *addr, size_t index );
+JNL_API void jnl_listen_release( jnl_listen_t listen );
+JNL_API jnl_connection_t jnl_listen_get_connection( jnl_listen_t listen );
+JNL_API unsigned short jnl_listen_get_port( jnl_listen_t listen );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif//_JNETLIB_H_
diff --git a/Src/replicant/jnetlib/jnetlib.sln b/Src/replicant/jnetlib/jnetlib.sln
new file mode 100644
index 000000000..a8e81332f
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib.sln
@@ -0,0 +1,54 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/jnetlib/jnetlib.vcxproj b/Src/replicant/jnetlib/jnetlib.vcxproj
new file mode 100644
index 000000000..c0913b228
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib.vcxproj
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{E105A0A2-7391-47C5-86AC-718003524C3D}</ProjectGuid>
+ <RootNamespace>jnetlib</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;JNETLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>ws2_32.lib;Crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="asyncdns.cpp" />
+ <ClCompile Include="connection.cpp" />
+ <ClCompile Include="Headers.cpp" />
+ <ClCompile Include="httpget.cpp" />
+ <ClCompile Include="httpserv.cpp" />
+ <ClCompile Include="httpuserv.cpp" />
+ <ClCompile Include="jnetlib.cpp" />
+ <ClCompile Include="listen.cpp" />
+ <ClCompile Include="multicastlisten.cpp" />
+ <ClCompile Include="sslconnection.cpp" />
+ <ClCompile Include="udpconnection.cpp" />
+ <ClCompile Include="util.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="asyncdns.h" />
+ <ClInclude Include="connection.h" />
+ <ClInclude Include="Headers.h" />
+ <ClInclude Include="httpget.h" />
+ <ClInclude Include="httpserv.h" />
+ <ClInclude Include="httpuserv.h" />
+ <ClInclude Include="jnetlib.h" />
+ <ClInclude Include="jnetlib_defines.h" />
+ <ClInclude Include="listen.h" />
+ <ClInclude Include="multicastlisten.h" />
+ <ClInclude Include="netinc.h" />
+ <ClInclude Include="sslconnection.h" />
+ <ClInclude Include="udpconnection.h" />
+ <ClInclude Include="util.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="jnetlib-replicant.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\nu\nu.vcxproj">
+ <Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/jnetlib/jnetlib.vcxproj.filters b/Src/replicant/jnetlib/jnetlib.vcxproj.filters
new file mode 100644
index 000000000..09c9c0575
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib.vcxproj.filters
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Headers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="jnetlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="multicastlisten.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="connection.cpp">
+ <Filter>Source Files\Connection</Filter>
+ </ClCompile>
+ <ClCompile Include="asyncdns.cpp">
+ <Filter>Source Files\DNS</Filter>
+ </ClCompile>
+ <ClCompile Include="sslconnection.cpp">
+ <Filter>Source Files\Connection</Filter>
+ </ClCompile>
+ <ClCompile Include="httpget.cpp">
+ <Filter>Source Files\HTTP Receiver</Filter>
+ </ClCompile>
+ <ClCompile Include="httpserv.cpp">
+ <Filter>Source Files\HTTP Server</Filter>
+ </ClCompile>
+ <ClCompile Include="udpconnection.cpp">
+ <Filter>Source Files\UDP Connection</Filter>
+ </ClCompile>
+ <ClCompile Include="listen.cpp">
+ <Filter>Source Files\Web Server</Filter>
+ </ClCompile>
+ <ClCompile Include="httpuserv.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Headers.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="httpuserv.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="jnetlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="jnetlib_defines.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="netinc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="connection.h">
+ <Filter>Header Files\Connection</Filter>
+ </ClInclude>
+ <ClInclude Include="asyncdns.h">
+ <Filter>Header Files\DNS</Filter>
+ </ClInclude>
+ <ClInclude Include="sslconnection.h">
+ <Filter>Header Files\Connection</Filter>
+ </ClInclude>
+ <ClInclude Include="httpget.h">
+ <Filter>Header Files\HTTP Receiver</Filter>
+ </ClInclude>
+ <ClInclude Include="httpserv.h">
+ <Filter>Header Files\HTTP Server</Filter>
+ </ClInclude>
+ <ClInclude Include="listen.h">
+ <Filter>Header Files\Web Server</Filter>
+ </ClInclude>
+ <ClInclude Include="udpconnection.h">
+ <Filter>Header Files\UDP Connection</Filter>
+ </ClInclude>
+ <ClInclude Include="multicastlisten.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{bcdc816d-5cef-49ce-bcae-e765d84d9d0d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{22cf6f69-c499-4eb0-851c-e405c0343744}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{ebc9b8af-fa9f-4cb3-bd28-a6f2fa43f47a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Connection">
+ <UniqueIdentifier>{08aa3960-7673-4eee-a554-f75b30f6ca9d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\DNS">
+ <UniqueIdentifier>{2194f281-7614-4fce-ae5b-14ebe64c75df}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\HTTP Receiver">
+ <UniqueIdentifier>{2a2b5cdf-93f4-4228-ab77-50138a1df782}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\HTTP Server">
+ <UniqueIdentifier>{c3f9a1b8-ab75-4d41-b614-df702b4a9113}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\UDP Connection">
+ <UniqueIdentifier>{080bb148-d8bc-41d5-b2a2-405b3068b15f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Web Server">
+ <UniqueIdentifier>{2208b2ff-bd0e-42a9-b60b-fb6c1be6bad8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Connection">
+ <UniqueIdentifier>{ab26ced4-713d-4b15-bea7-49219fe68955}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\DNS">
+ <UniqueIdentifier>{336ee3a1-044b-42c0-9f39-f03f381c73ee}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\HTTP Receiver">
+ <UniqueIdentifier>{b7051bc5-90cf-4673-8274-02c12e3da56c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\HTTP Server">
+ <UniqueIdentifier>{48e64e69-bc76-467b-8fd3-3430694896ee}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Web Server">
+ <UniqueIdentifier>{ad8b2861-d8e2-4737-b293-124ad361f20c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\UDP Connection">
+ <UniqueIdentifier>{60bab4c5-d369-4284-b314-dd8c3c4dcd37}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="jnetlib-replicant.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/jnetlib/jnetlib_defines.h b/Src/replicant/jnetlib/jnetlib_defines.h
new file mode 100644
index 000000000..76647604c
--- /dev/null
+++ b/Src/replicant/jnetlib/jnetlib_defines.h
@@ -0,0 +1,66 @@
+#pragma once
+#include "../foundation/types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Connection */
+typedef struct jnl_connection_struct jnl_connection_struct;
+typedef jnl_connection_struct *jnl_connection_t;
+
+enum
+{
+ JNL_CONNECTION_STATE_ERROR = 0,
+ JNL_CONNECTION_STATE_NOCONNECTION = 1,
+ JNL_CONNECTION_STATE_RESOLVING = 2,
+ JNL_CONNECTION_STATE_CONNECTING = 3,
+ JNL_CONNECTION_STATE_CONNECTED = 4,
+ JNL_CONNECTION_STATE_CLOSING = 5,
+ JNL_CONNECTION_STATE_CLOSED = 6,
+ JNL_CONNECTION_STATE_RESOLVED = 7, // happens after RESOLVING, but going here for compatability
+};
+
+/* UDP */
+typedef struct jnl_udp_struct jnl_udp_struct;
+typedef jnl_udp_struct *jnl_udp_t;
+
+/* HTTP */
+typedef struct jnl_http_struct jnl_http_struct;
+typedef jnl_http_struct *jnl_http_t;
+
+enum
+{
+ HTTPGET_STATUS_ERROR = -1,
+ JNL_HTTP_STATUS_ERROR = HTTPGET_STATUS_ERROR,
+ HTTPGET_STATUS_CONNECTING = 0,
+ JNL_HTTP_STATUS_CONNECTING = HTTPGET_STATUS_CONNECTING,
+ HTTPGET_STATUS_READING_HEADERS = 1,
+ JNL_HTTP_STATUS_READING_HEADERS = HTTPGET_STATUS_READING_HEADERS,
+ HTTPGET_STATUS_READING_CONTENT = 2,
+ JNL_HTTP_STATUS_READING_CONTENT = HTTPGET_STATUS_READING_CONTENT,
+};
+
+enum
+{
+ HTTPGET_RUN_ERROR = -1,
+ HTTPGET_RUN_OK = 0,
+ JNL_HTTP_RUN_OK = HTTPGET_RUN_OK,
+ HTTPGET_RUN_CONNECTION_CLOSED = 1,
+};
+
+/* DNS */
+typedef struct jnl_dns_struct jnl_dns_struct;
+typedef jnl_dns_struct *jnl_dns_t;
+
+typedef struct jnl_httpu_request_struct jnl_httpu_request_struct;
+typedef jnl_httpu_request_struct *jnl_httpu_request_t;
+
+typedef struct jnl_http_request_struct jnl_http_request_struct;
+typedef jnl_http_request_struct *jnl_http_request_t;
+
+typedef struct jnl_listen_struct jnl_listen_struct;
+typedef jnl_listen_struct *jnl_listen_t;
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/jnetlib/listen.cpp b/Src/replicant/jnetlib/listen.cpp
new file mode 100644
index 000000000..20d34d06f
--- /dev/null
+++ b/Src/replicant/jnetlib/listen.cpp
@@ -0,0 +1,131 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2013 Nullsoft, Inc.
+** Author: Justin Frankel, Ben Allison
+** File: listen.cpp - JNL TCP listen implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "listen.h"
+#include "foundation/error.h"
+
+JNL_Listen::JNL_Listen()
+{
+ m_port=0;
+ m_socket=-1;
+}
+
+int JNL_Listen::Initialize(unsigned short port, sockaddr *which_interface, int family)
+{
+ m_port = port;
+
+ char portString[32] = {0};
+ sprintf(portString, "%d", (int)port);
+
+ addrinfo *res;
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+ hints.ai_addr = which_interface?which_interface:INADDR_ANY;
+
+ if (getaddrinfo(NULL, portString, &hints, &res) == 0)
+ {
+ int ret = Initialize(res, 0);
+ freeaddrinfo(res);
+ return ret;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+}
+
+int JNL_Listen::Initialize(addrinfo *address, size_t index)
+{
+ addrinfo *res = address;
+
+ while (index--)
+ {
+ res = res->ai_next;
+ if (!res)
+ return NErr_EndOfEnumeration;
+ }
+
+ m_socket = ::socket(res->ai_family,res->ai_socktype, res->ai_protocol);
+ if (m_socket < 0)
+ {
+ freeaddrinfo(res);
+ return NErr_Error;
+ }
+ else
+ {
+ SET_SOCK_BLOCK(m_socket,0);
+#ifndef _WIN32
+ int bflag = 1;
+ setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &bflag, sizeof(bflag));
+#endif
+ if (::bind(m_socket, res->ai_addr, (int)res->ai_addrlen))
+ {
+ closesocket(m_socket);
+ m_socket=-1;
+ return NErr_Error;
+ }
+ else if (::listen(m_socket,8)==-1)
+ {
+ closesocket(m_socket);
+ m_socket=-1;
+ return NErr_Error;
+ }
+ }
+ return NErr_Success;
+}
+
+JNL_Listen::~JNL_Listen()
+{
+ if (m_socket>=0)
+ {
+ closesocket(m_socket);
+ }
+}
+
+JNL_Connection *JNL_Listen::get_connect(size_t sendbufsize, size_t recvbufsize)
+{
+ if (m_socket < 0)
+ {
+ return NULL;
+ }
+ sockaddr_storage saddr;
+ socklen_t length = sizeof(saddr);
+ SOCKET s = accept(m_socket, (sockaddr *)&saddr, &length);
+ if (s != -1)
+ {
+ JNL_Connection *c=new JNL_Connection(NULL,sendbufsize, recvbufsize);
+ c->connect(s, (sockaddr *)&saddr, length);
+ return c;
+ }
+
+ return NULL;
+}
+
+socklen_t JNL_Listen::get_address(sockaddr* address, socklen_t *address_len)
+{
+ return getsockname(m_socket, address, address_len);
+}
+
+unsigned short JNL_Listen::get_port()
+{
+ if (!m_port)
+ {
+ sockaddr_in address;
+ socklen_t namelen = sizeof(address);
+ if (getsockname(m_socket, (sockaddr *)&address, &namelen) == 0)
+ m_port = ntohs(address.sin_port);
+ }
+
+ return m_port;
+} \ No newline at end of file
diff --git a/Src/replicant/jnetlib/listen.h b/Src/replicant/jnetlib/listen.h
new file mode 100644
index 000000000..dc8cfc180
--- /dev/null
+++ b/Src/replicant/jnetlib/listen.h
@@ -0,0 +1,44 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2013 Nullsoft, Inc.
+** Author: Justin Frankel, Ben Allison
+** File: listen.h - JNL interface for opening a TCP listen
+** License: see jnetlib.h
+**
+** Usage:
+** 1. create a JNL_Listen object with the port and (optionally) the interface
+** to listen on.
+** 2. call get_connect() to get any new connections (optionally specifying what
+** buffer sizes the connection should be created with)
+** 3. check is_error() to see if an error has occured
+** 4. call port() if you forget what port the listener is on.
+**
+*/
+
+#ifndef _LISTEN_H_
+#define _LISTEN_H_
+
+#include "connection.h"
+#include "nswasabi/ReferenceCounted.h"
+
+class JNL_Listen : public ReferenceCountedBase<JNL_Listen>
+{
+public:
+ JNL_Listen();
+ ~JNL_Listen();
+
+ int Initialize( unsigned short port, sockaddr *which_interface = 0, int family = PF_UNSPEC );
+ int Initialize( struct addrinfo *address, size_t index );
+
+ JNL_Connection *get_connect( size_t sendbufsize = 8192, size_t recvbufsize = 8192 );
+ unsigned short get_port();
+ int is_error( void ) { return ( m_socket < 0 ); }
+
+ socklen_t get_address( sockaddr *address, socklen_t *address_len );
+
+protected:
+ SOCKET m_socket;
+ unsigned short m_port;
+};
+
+#endif //_LISTEN_H_
diff --git a/Src/replicant/jnetlib/multicastlisten.cpp b/Src/replicant/jnetlib/multicastlisten.cpp
new file mode 100644
index 000000000..21eea33ce
--- /dev/null
+++ b/Src/replicant/jnetlib/multicastlisten.cpp
@@ -0,0 +1,123 @@
+/*
+** JNetLib
+** Copyright (C) 2008 Nullsoft, Inc.
+** Author: Ben Allison
+** File: multicastlisten.cpp - JNL Multicast UDP listen implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "multicastlisten.h"
+#include "foundation/error.h"
+#include <new>
+
+int CreateMulticastListener(JNL_UDPConnection **connection, const char *mcast_ip, unsigned short port, size_t sendbufsize, size_t recvbufsize)
+{
+ char portString[32] = {0};
+
+ if (port)
+ sprintf(portString, "%d", (int)port);
+
+ addrinfo *res=0;
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET; /* IPv4 only for now until we get IPv6 multicast registration working */
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+ hints.ai_protocol = IPPROTO_UDP;
+
+ if (getaddrinfo(NULL, port?portString:0, &hints, &res) == 0)
+ {
+ SOCKET m_socket = ::socket(res->ai_family,res->ai_socktype, res->ai_protocol);
+ if (m_socket < 0)
+ {
+ freeaddrinfo(res);
+ return NErr_Error;
+ }
+ else
+ {
+ SET_SOCK_BLOCK(m_socket,0);
+
+ int bflag = 1;
+ if (setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&bflag, sizeof(bflag)))
+ {
+ /*int err = ERRNO;
+ err=err;
+ printf("SO_REUSEADDR error: %d\n", err);*/
+ }
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ bflag=1; // in case it magically got unset above
+ setsockopt(m_socket, SOL_SOCKET, SO_REUSEPORT, (const char *)&bflag, sizeof(bflag));
+#endif
+ if (::bind(m_socket, res->ai_addr, (int)res->ai_addrlen))
+ {
+ closesocket(m_socket);
+ return NErr_Error;
+ }
+ else
+ {
+ // TODO: ipv6 with IPV6_ADD_MEMBERSHIP and ipv6_mreq
+
+ sockaddr_in *ipv4 = (sockaddr_in *)res->ai_addr;
+
+ /* join multicast group */
+ ip_mreq ssdpMcastAddr;
+ memset(&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
+ ssdpMcastAddr.imr_interface = ipv4->sin_addr;
+ ssdpMcastAddr.imr_multiaddr.s_addr = inet_addr(mcast_ip);
+ if (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&ssdpMcastAddr, sizeof(ssdpMcastAddr)))
+ {
+ closesocket(m_socket);
+ freeaddrinfo(res);
+ return NErr_Error;
+ }
+
+ /* Set multicast interface. */
+ in_addr addr;
+ memset(&addr, 0, sizeof(addr));
+ addr = ipv4->sin_addr;
+ if (setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&addr, sizeof(addr)))
+ {
+ /*int err = ERRNO;
+ err=err;
+ printf("IP_MULTICAST_IF error: %d\n", err);*/
+ /* This is probably not a critical error, so let's continue. */
+ }
+
+ /* set TTL to 4 */
+ uint8_t ttl=4;
+ if (setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ttl, sizeof(ttl)))
+ {
+ /*int err = ERRNO;
+ err=err;
+ printf("IP_MULTICAST_TTL error: %d\n", err);*/
+ /* This is probably not a critical error, so let's continue. */
+ }
+
+ int option = 1;
+ if (setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, (const char *)&option, sizeof(option)) != 0)
+ {
+ closesocket(m_socket);
+ freeaddrinfo(res);
+ return NErr_Error;
+ }
+ }
+ }
+ freeaddrinfo(res);
+ JNL_UDPConnection *c=new (std::nothrow) JNL_UDPConnection();
+ if (!c)
+ {
+ closesocket(m_socket);
+ return NErr_OutOfMemory;
+ }
+ c->open((int)m_socket, NULL, sendbufsize, recvbufsize);
+ *connection = c;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+}
+
diff --git a/Src/replicant/jnetlib/multicastlisten.h b/Src/replicant/jnetlib/multicastlisten.h
new file mode 100644
index 000000000..eaa918a18
--- /dev/null
+++ b/Src/replicant/jnetlib/multicastlisten.h
@@ -0,0 +1,26 @@
+/*
+** JNetLib
+** Copyright (C) 2008 Nullsoft, Inc.
+** Author: Ben Allison
+** File: multicastlisten.h - JNL interface for opening a Multicast UDP listen
+** License: see jnetlib.h
+**
+** Usage:
+** 1. create a JNL_MulticastUDPListen object with the port and (optionally) the interface
+** to listen on.
+** 2. call get_connect() to get the associated UDP connection (optionally specifying what
+** buffer sizes the connection should be created with). Unlike TCP listen, there is only one listen object.
+** 3. check is_error() to see if an error has occured
+** 4. call port() if you forget what port the listener is on.
+**
+*/
+
+#ifndef _MULTICASTLISTEN_H_
+#define _MULTICASTLISTEN_H_
+
+#include "udpconnection.h"
+
+
+int CreateMulticastListener( JNL_UDPConnection **connection, const char *mcast_ip, unsigned short port, size_t sendbufsize = 8192, size_t recvbufsize = 8192 );
+
+#endif //_LISTEN_H_
diff --git a/Src/replicant/jnetlib/netinc.h b/Src/replicant/jnetlib/netinc.h
new file mode 100644
index 000000000..1952d3d7d
--- /dev/null
+++ b/Src/replicant/jnetlib/netinc.h
@@ -0,0 +1,84 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2006 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: netinc.h - network includes and portability defines (used internally)
+** License: see jnetlib.h
+*/
+
+#ifndef _NETINC_H_
+#define _NETINC_H_
+
+#ifdef _WIN32
+
+//#include <time.h>
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+#include <wspiapi.h>
+#include "../foundation/types.h"
+
+#define strtoull(x,y,z) _strtoui64(x,y,z)
+#define strcasecmp(x,y) _stricmp(x,y)
+#define strcasecmpn(x,y, count) _strnicmp(x,y,count)
+#define strncasecmp(x,y, count) _strnicmp(x,y,count)
+#define HTONS(val) ((((unsigned short) (val) & (unsigned short) 0x00ffU) << 8) | (((unsigned short) (val) & (unsigned short) 0xff00U) >> 8))
+#define ERRNO (WSAGetLastError())
+#define SET_SOCK_BLOCK(s,block) { unsigned long __i=block?0:1; ioctlsocket(s,FIONBIO,&__i); }
+#define JNL_EWOULDBLOCK WSAEWOULDBLOCK
+#define JNL_EINPROGRESS WSAEWOULDBLOCK
+
+#else
+#define JNL_EWOULDBLOCK EWOULDBLOCK
+#define JNL_EINPROGRESS EINPROGRESS
+#ifndef THREAD_SAFE
+#define THREAD_SAFE
+#endif
+#ifndef _REENTRANT
+#define _REENTRANT
+#endif
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+
+#define ERRNO errno
+#define closesocket(s) close(s)
+#define SET_SOCK_BLOCK(s,block) { int __flags; if ((__flags = fcntl(s, F_GETFL, 0)) != -1) { if (!block) __flags |= O_NONBLOCK; else __flags &= ~O_NONBLOCK; fcntl(s, F_SETFL, __flags); } }
+
+#define _stricmp(x,y) strcasecmp(x,y)
+#define _strnicmp(x,y,z) strncasecmp(x,y,z)
+#define wsprintf sprintf
+typedef int SOCKET;
+
+#endif // !_WIN32
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef INADDR_ANY
+#define INADDR_ANY 0
+#endif
+
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
+
+#endif //_NETINC_H_
diff --git a/Src/replicant/jnetlib/resource.h b/Src/replicant/jnetlib/resource.h
new file mode 100644
index 000000000..b6cf148a8
--- /dev/null
+++ b/Src/replicant/jnetlib/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by jnetlib-replicant.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/replicant/jnetlib/sslconnection.cpp b/Src/replicant/jnetlib/sslconnection.cpp
new file mode 100644
index 000000000..5362c445d
--- /dev/null
+++ b/Src/replicant/jnetlib/sslconnection.cpp
@@ -0,0 +1,329 @@
+//#ifdef USE_SSL
+//#include "netinc.h"
+//#include "util.h"
+//#include "connection.h"
+#include "sslconnection.h"
+
+SSL_CTX *sslContext = 0;
+
+#ifdef _DEBUG
+
+extern "C" void apps_ssl_info_callback (const SSL * s, int where, int ret)
+{
+ /*
+ ** DEBUG INFO HERE
+ */
+}
+#endif
+
+/*
+** Well, you should probably change this based on like...
+** well, you're level of trust heh
+** For now, this basically trusts all certs :)
+**
+*/
+#if 0
+extern "C" int verify_callback(int ok, X509_STORE_CTX * ctx)
+{
+ /* For certificate verification */
+ int verify_depth = 0;
+ int verify_error = X509_V_OK;
+
+ char buf[1024] = {0};
+ X509 * err_cert = X509_STORE_CTX_get_current_cert(ctx);
+ int err = X509_STORE_CTX_get_error(ctx);
+ int depth = X509_STORE_CTX_get_error_depth(ctx);
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+ if (!ok)
+ {
+ if (verify_depth >= depth)
+ {
+ ok = 1;
+ verify_error = X509_V_OK;
+ }
+ else
+ {
+ ok = 0;
+ verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ }
+ }
+ switch (ctx->error)
+ {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf));
+ break;
+
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ break;
+
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ break;
+ }
+ return ok;
+}
+#endif
+
+JNL_SSL_Connection::JNL_SSL_Connection() : forceConnect(false), m_ssl(0), m_bsslinit(false), m_bcontextowned(true)
+{
+ m_bsslinit = true;
+}
+
+
+JNL_SSL_Connection::JNL_SSL_Connection(SSL* pssl, JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize) : JNL_Connection(dns, sendbufsize, recvbufsize),
+ forceConnect(false)
+{
+ m_ssl = pssl;
+ m_bsslinit = false;
+
+ if (m_ssl)
+ {
+ m_bcontextowned = false;
+ }
+ else
+ {
+ m_bcontextowned = true;
+ }
+
+ if (m_bcontextowned == false)
+ {
+ return ;
+ }
+
+ m_bsslinit = true;
+
+
+ /* See the SSL states in our own callback */
+#ifdef _DEBUG
+ // SSL_CTX_set_info_callback(m_app_ctx, apps_ssl_info_callback);
+#endif
+
+ /* Set the certificate verification callback */
+ //SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER, verify_callback);
+
+ /* Not sure what this does */
+ //SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_CLIENT);
+
+ return ;
+}
+
+int JNL_SSL_Connection::socket_connect()
+{
+ int retval;
+
+ if (m_bcontextowned == false)
+ {
+ /*
+ ** WTF?
+ */
+ return -1;
+ }
+ if (m_ssl != NULL)
+ {
+ return -1;
+ }
+
+ retval = JNL_Connection::socket_connect();
+
+ if (retval != 0)
+ {
+ if (ERRNO != JNL_EINPROGRESS)
+ {
+ return retval; // benski> if the underlying socket hasn't connected yet, then we can't start the SSL connection
+ /*
+ ** Joshua Teitelbaum 3/2/2006
+ ** Fatal error here
+ */
+ }
+ }
+
+ // moved from InitSSL() as no need to create this unless
+ // we're actually going to use it which helps slow loads
+ if (!sslContext)
+ {
+ sslContext = SSL_CTX_new(SSLv23_client_method());
+ if (sslContext)
+ {
+ SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ m_ssl = SSL_new(sslContext);
+
+ if (m_ssl == NULL)
+ {
+ return -1;
+ }
+
+ /* Tell that we are in connect mode */
+ SSL_set_connect_state(m_ssl);
+
+ /* Set socket descriptor with the socket we already have open */
+ if(SSL_set_fd(m_ssl, m_socket) != 0)
+ {
+ return -1;
+ }
+
+ return retval;
+}
+
+void JNL_SSL_Connection::socket_shutdown()
+{
+ if (m_ssl)
+ SSL_shutdown(m_ssl);
+ JNL_Connection::socket_shutdown();
+
+ if (m_ssl)
+ {
+ SSL_free(m_ssl);
+ m_ssl = NULL;
+ }
+ return ;
+}
+
+void JNL_SSL_Connection::run(size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd)
+{
+ if (!m_bsslinit)
+ {
+ int rval = SSL_accept(m_ssl);
+ if (rval == -1)
+ {
+ int e = SSL_get_error(m_ssl, rval);
+
+ if (!((e == SSL_ERROR_WANT_READ) || (e == SSL_ERROR_WANT_WRITE)))
+ {
+ m_state = STATE_ERROR;
+ }
+
+ return ;
+ }
+ else
+ {
+ m_bsslinit = true;
+ }
+ }
+
+ /**
+ ** benski - march 2, 2006
+ **if the underlying socket didn't connected yet, we need to try the SSL connection again
+ */
+ if (forceConnect)
+ {
+ if(init_ssl_connection() == false)
+ {
+ return;
+ }
+ }
+ JNL_Connection::run(max_send_bytes, max_recv_bytes, bytes_sent, bytes_rcvd);
+}
+
+/*
+** init_ssl_connection:
+** Returns true, meaning can continue
+** Else, cannot continue with underlying run
+** side effects:
+** sets forceConnect
+*/
+bool JNL_SSL_Connection::init_ssl_connection()
+{
+ if(m_ssl == NULL)
+ {
+ /*
+ ** WTF?
+ ** cascade up.
+ */
+ return true;
+ }
+
+ int retval = SSL_connect(m_ssl);
+
+ if (retval < 0)
+ {
+ int err = SSL_get_error(m_ssl, retval);
+
+ switch (err)
+ {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_CONNECT:
+ forceConnect = true;
+ break;
+ // fall through
+ default: // TODO: benski> MOST other errors are OK, (especially "want read" and "want write", but we need to think through all the scenarios here
+ forceConnect=false;
+ }
+ }
+ else if(retval)
+ {
+ /*
+ ** success
+ */
+ forceConnect = false;
+ }
+
+ /*
+ ** If retval == 0
+ ** socket is closed, or serious error occurred.
+ ** cascade up.
+ */
+
+ return forceConnect==false;
+}
+
+void JNL_SSL_Connection::on_socket_connected(void)
+{
+ init_ssl_connection();
+}
+
+void JNL_SSL_Connection::connect(SOCKET s, sockaddr *addr, socklen_t length)
+{
+ /*
+ ** Joshua Teitelbaum 2/01/2006
+ ** No need to close
+ ** This is the reason for divergence as well as setting
+ ** the connect state
+ */
+
+ m_socket = s;
+ address=(sockaddr *)malloc(length);
+ memcpy(address, addr, length);
+
+ m_remote_port = 0;
+ if (m_socket != -1)
+ {
+ SET_SOCK_BLOCK(m_socket, 0);
+ m_state = STATE_CONNECTED;
+ SSL_set_fd(m_ssl, m_socket);
+ }
+ else
+ {
+ m_errorstr = "invalid socket passed to connect";
+ m_state = STATE_ERROR;
+ }
+}
+
+ssize_t JNL_SSL_Connection::socket_recv(char *buf, size_t len, int options)
+{
+ return SSL_read(m_ssl, buf, (int)len);
+}
+
+ssize_t JNL_SSL_Connection::socket_send(const char *buf, size_t len, int options)
+{
+ return SSL_write(m_ssl, buf, (int)len);
+}
+
+JNL_SSL_Connection::~JNL_SSL_Connection()
+{
+ if (m_ssl)
+ {
+ SSL_free(m_ssl);
+ m_ssl = NULL;
+ }
+}
+//#endif \ No newline at end of file
diff --git a/Src/replicant/jnetlib/sslconnection.h b/Src/replicant/jnetlib/sslconnection.h
new file mode 100644
index 000000000..f6e54d05e
--- /dev/null
+++ b/Src/replicant/jnetlib/sslconnection.h
@@ -0,0 +1,61 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2006 CockOS, Inc.
+** Author: Justin Frankel, Joshua Teitelbaum
+** File: sslconnection.h - JNL SSL TCP connection interface
+** License: see jnetlib.h
+*/
+
+#ifndef _JNETLIB_SSL_H_
+#define _JNETLIB_SSL_H_
+
+#include "netinc.h"
+#include "util.h"
+#include "connection.h"
+
+/*
+** AUTOLINK WITH THESE GUYS NOT IN PROJECT HEH HEH :)
+** Build and go, for you :)
+*/
+
+#include <openssl/ssl.h>
+
+class JNL_SSL_Connection : public JNL_Connection
+{
+protected:
+ SSL *m_ssl;
+ bool m_bcontextowned;
+ bool m_bsslinit;
+
+public:
+ JNL_SSL_Connection();
+ JNL_SSL_Connection( SSL *pssl, JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize );
+ virtual ~JNL_SSL_Connection();
+
+ virtual void connect( SOCKET sock, sockaddr *addr, socklen_t length /* of addr */ ); // used by the listen object, usually not needed by users.
+ virtual void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL );
+
+ /*
+ ** Joshua Teitelbaum 1/27/2006 Adding new BSD socket analogues for SSL compatibility
+ */
+protected:
+ virtual void socket_shutdown();
+ virtual ssize_t socket_recv( char *buf, size_t len, int options );
+ virtual ssize_t socket_send( const char *buf, size_t len, int options );
+ virtual int socket_connect();
+ virtual void on_socket_connected();
+ /*
+ ** init_ssl_connection:
+ ** returns true if can continue onwards (could be error, in which case
+ ** we want the error to cascade through).
+ ** Else, false if connection was not made, and we have to call this
+ ** again.
+ */
+ bool init_ssl_connection();
+
+ bool forceConnect;
+};
+
+extern SSL_CTX *sslContext;
+
+#endif
diff --git a/Src/replicant/jnetlib/udpconnection.cpp b/Src/replicant/jnetlib/udpconnection.cpp
new file mode 100644
index 000000000..fbb36eca6
--- /dev/null
+++ b/Src/replicant/jnetlib/udpconnection.cpp
@@ -0,0 +1,384 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2001 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: udpconnection.cpp - JNL UDP connection implementation
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "udpconnection.h"
+
+JNL_UDPConnection::JNL_UDPConnection(unsigned short incoming_port, JNL_AsyncDNS *dns, int sendbufsize, int recvbufsize)
+{
+ init();
+ open(dns, sendbufsize, recvbufsize);
+
+ m_socket=::socket(PF_INET,SOCK_DGRAM,0);
+ if (m_socket==-1)
+ {
+ m_errorstr="creating socket";
+ m_state=STATE_ERROR;
+ }
+ SET_SOCK_BLOCK(m_socket,0);
+ sockaddr_in m_iaddr;
+ memset(&m_iaddr,0,sizeof(struct sockaddr_in));
+ m_iaddr.sin_family=AF_INET;
+ m_iaddr.sin_port=htons(incoming_port);
+ m_iaddr.sin_addr.s_addr = htonl( INADDR_ANY );
+ if(::bind(m_socket,(struct sockaddr *)&m_iaddr,sizeof(m_iaddr))==-1)
+ {
+ m_errorstr="binding socket";
+ m_state=STATE_ERROR;
+ }
+ m_state=STATE_CONNECTED;
+}
+
+JNL_UDPConnection::JNL_UDPConnection()
+{
+ init();
+}
+
+void JNL_UDPConnection::init()
+{
+ m_errorstr="";
+ address=0;
+ address_len=0;
+ m_dns=0;
+ m_dns_owned=false;
+ m_socket=-1;
+ m_remote_port=0;
+ m_state=STATE_NOCONNECTION;
+ m_host=0;
+ saddr=0;
+ m_last_addr_len=0;
+ ttl=0;
+}
+
+void JNL_UDPConnection::set_ttl(uint8_t new_ttl)
+{
+ ttl=new_ttl;
+ setsockopt(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ttl, sizeof(ttl));
+}
+
+void JNL_UDPConnection::open(int incoming_socket, JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize)
+{
+ if (dns != JNL_AUTODNS && dns)
+ {
+ m_dns=dns;
+ m_dns_owned=false;
+ }
+ else if (!m_dns)
+ {
+ m_dns=new JNL_AsyncDNS;
+ m_dns_owned=true;
+ }
+ recv_buffer.reserve(recvbufsize);
+ send_buffer.reserve(sendbufsize);
+
+ m_socket=incoming_socket;
+ m_state=STATE_CONNECTED;
+
+}
+
+void JNL_UDPConnection::open(JNL_AsyncDNS *dns, size_t sendbufsize, size_t recvbufsize)
+{
+ if (dns != JNL_AUTODNS && dns)
+ {
+ m_dns=dns;
+ m_dns_owned=false;
+ }
+ else if (!m_dns)
+ {
+ m_dns=new JNL_AsyncDNS;
+ m_dns_owned=true;
+ }
+ recv_buffer.reserve(recvbufsize);
+ send_buffer.reserve(sendbufsize);
+}
+
+void JNL_UDPConnection::setpeer(const char *hostname, int port)
+{
+ m_remote_port=(unsigned short)port;
+ m_host = _strdup(hostname);
+
+ if (!m_host || !m_host[0])
+ {
+ m_errorstr="empty hostname";
+ m_state=STATE_ERROR;
+ }
+ else
+ {
+ m_state=STATE_RESOLVING;
+ }
+}
+
+void JNL_UDPConnection::setpeer(sockaddr *addr, socklen_t length /* of addr */)
+{
+ //memcpy(&m_saddr, addr, sizeof(sockaddr));
+ free(address);
+ address_len=length;
+ address=(sockaddr *)malloc(length);
+ memcpy(address, addr, length);
+}
+
+JNL_UDPConnection::~JNL_UDPConnection()
+{
+ if (m_socket >= 0)
+ {
+ ::closesocket(m_socket);
+ m_socket=-1;
+ }
+ if (!saddr) // free it if it was passed to us (by JNL_Listen, presumably)
+ free(address); // TODO: change this if we ever do round-robin DNS connecting or in any way change how we handle 'address'
+
+ if (m_dns_owned)
+ {
+ delete static_cast<JNL_AsyncDNS *>(m_dns);
+ }
+ free(m_host);
+}
+
+void JNL_UDPConnection::run(size_t max_send_bytes, size_t max_recv_bytes, size_t *bytes_sent, size_t *bytes_rcvd)
+{
+ size_t bytes_allowed_to_send=(max_send_bytes<0)?send_buffer.size():max_send_bytes;
+ size_t bytes_allowed_to_recv=(max_recv_bytes<0)?recv_buffer.avail():max_recv_bytes;
+
+ if (bytes_sent) *bytes_sent=0;
+ if (bytes_rcvd) *bytes_rcvd=0;
+
+ switch (m_state)
+ {
+ case STATE_RESOLVING:
+ if (saddr == 0)
+ {
+ int a=m_dns->resolve(m_host, m_remote_port, &saddr, SOCK_DGRAM);
+ if (!a)
+ {
+ address=saddr->ai_addr;
+ address_len=(socklen_t)saddr->ai_addrlen;
+ m_state=STATE_CONNECTED;
+ }
+ else if (a == 1)
+ {
+ m_state=STATE_RESOLVING;
+ break;
+ }
+ else
+ {
+ m_errorstr="resolving hostname";
+ m_state=STATE_ERROR;
+ return;
+ }
+ }
+ break;
+ case STATE_CONNECTED:
+ case STATE_CLOSING:
+ if (!send_buffer.empty() && bytes_allowed_to_send>0)
+ {
+ size_t sent = send_buffer.drain(this, bytes_allowed_to_send);
+ if (bytes_sent)
+ *bytes_sent+=sent;
+ }
+ /* only read from socket when buffer is empty
+ * otherwise we risk data loss
+ * see "man 2 recvfrom" for details
+ */
+ if (recv_buffer.empty() && bytes_allowed_to_recv)
+ {
+ /*
+ * use LockBuffer()/UnlockBuffer() because
+ * "wrap-around" reads can't be done
+ * we might read data from two separate packets
+ */
+ size_t len = recv_buffer.avail();
+ if (bytes_allowed_to_recv < len)
+ len = bytes_allowed_to_recv;
+
+ recv_buffer.clear();
+ void *buffer = recv_buffer.LockBuffer();
+ m_last_addr_len = (int)sizeof(m_last_addr);
+ int res=::recvfrom(m_socket,(char *)buffer,(int)len,0,(sockaddr *)&m_last_addr,&m_last_addr_len);
+ if (res == 0 || (res < 0 && ERRNO != JNL_EWOULDBLOCK))
+ {
+ recv_buffer.UnlockBuffer(0);
+ m_state=STATE_CLOSED;
+ break;
+ }
+ if (res > 0)
+ {
+ if (bytes_rcvd)
+ *bytes_rcvd+=res;
+ recv_buffer.UnlockBuffer(res);
+ }
+ else
+ recv_buffer.UnlockBuffer(0);
+
+ }
+ if (m_state == STATE_CLOSING)
+ {
+ if (send_buffer.empty()) m_state = STATE_CLOSED;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* RingBuffer client function */
+size_t JNL_UDPConnection::Read(void *dest, size_t len)
+{
+ if (!len)
+ return 0;
+
+ m_last_addr_len = (int)sizeof(m_last_addr);
+ int res=::recvfrom(m_socket, (char *)dest, (int)len, 0, (sockaddr *)&m_last_addr,&m_last_addr_len);
+ if (res == 0 || (res < 0 && ERRNO != JNL_EWOULDBLOCK))
+ {
+ m_state=STATE_CLOSED;
+ return 0;
+ }
+ if (res > 0)
+ return res;
+ else
+ return 0;
+}
+
+/* RingBuffer client function */
+size_t JNL_UDPConnection::Write(const void *dest, size_t len)
+{
+ if (!len)
+ return 0;
+ int res=::sendto(m_socket, (const char *)dest, (int)len, 0, address, address_len);
+ if (res==-1 && ERRNO != JNL_EWOULDBLOCK)
+ {
+ return 0;
+ // m_state=STATE_CLOSED;
+ }
+ if (res > 0)
+ return res;
+ else
+ return 0;
+}
+
+void JNL_UDPConnection::close(int quick)
+{
+ if (quick || m_state == STATE_RESOLVING)
+ {
+ m_state=STATE_CLOSED;
+ if (m_socket >= 0)
+ {
+ ::closesocket(m_socket);
+ }
+ m_socket=-1;
+ recv_buffer.clear();
+ send_buffer.clear();
+ m_remote_port=0;
+ free(m_host);
+ m_host=0;
+ //memset(&m_saddr,0,sizeof(m_saddr));
+ }
+ else
+ {
+ if (m_state == STATE_CONNECTED) m_state=STATE_CLOSING;
+ }
+}
+
+size_t JNL_UDPConnection::send_bytes_in_queue(void)
+{
+ return send_buffer.size();
+}
+
+size_t JNL_UDPConnection::send_bytes_available(void)
+{
+ return send_buffer.avail();
+}
+
+int JNL_UDPConnection::send(const void *data, size_t length)
+{
+ if (length > send_bytes_available())
+ {
+ return -1;
+ }
+
+ send_buffer.write(data, length);
+ return 0;
+}
+
+int JNL_UDPConnection::send_string(const char *line)
+{
+ return send(line,strlen(line));
+}
+
+size_t JNL_UDPConnection::recv_bytes_available(void)
+{
+ return recv_buffer.size();
+}
+
+size_t JNL_UDPConnection::peek_bytes(void *data, size_t maxlength)
+{
+ return recv_buffer.peek(data, maxlength);
+}
+
+size_t JNL_UDPConnection::recv_bytes(void *data, size_t maxlength)
+{
+ return recv_buffer.read(data, maxlength);
+}
+
+int JNL_UDPConnection::recv_lines_available(void)
+{
+ int l=(int)recv_bytes_available();
+ int lcount=0;
+ int lastch=0;
+ int pos;
+ for (pos=0; pos < l; pos ++)
+ {
+ char t;
+ if (recv_buffer.at(pos, &t, 1) != 1)
+ return lcount;
+ if ((t=='\r' || t=='\n') &&(
+ (lastch != '\r' && lastch != '\n') || lastch==t
+ )) lcount++;
+ lastch=t;
+ }
+ return lcount;
+}
+
+int JNL_UDPConnection::recv_line(char *line, size_t maxlength)
+{
+ if (maxlength > recv_buffer.size()) maxlength=recv_buffer.size();
+ while (maxlength--)
+ {
+ char t;
+ if (recv_buffer.read(&t, 1) == 0)
+ {
+ *line=0;
+ return 0;
+ }
+ if (t == '\r' || t == '\n')
+ {
+ char r;
+ if (recv_buffer.peek(&r, 1) != 0)
+ {
+ if ((r == '\r' || r == '\n') && r != t)
+ recv_buffer.advance(1);
+ }
+ *line=0;
+ return 0;
+ }
+ *line++=t;
+ }
+ return 1;
+}
+
+int JNL_UDPConnection::get_interface(sockaddr *sin, socklen_t *sin_length)
+{
+ if (m_socket==-1) return 1;
+ /*memset(sin,0,sizeof(sockaddr_storage));
+ *sin_length =sizeof(sockaddr_storage);*/
+ if (::getsockname(m_socket,(sockaddr *)sin,sin_length))
+ return 1;
+ return 0;
+}
+
diff --git a/Src/replicant/jnetlib/udpconnection.h b/Src/replicant/jnetlib/udpconnection.h
new file mode 100644
index 000000000..c91d84229
--- /dev/null
+++ b/Src/replicant/jnetlib/udpconnection.h
@@ -0,0 +1,151 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2001 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: udpconnection.h - JNL UDP connection interface
+** License: see jnetlib.h
+**
+** Usage:
+** 1. Create a JNL_Connection object, optionally specifying a JNL_AsyncDNS
+** object to use (or NULL for none, or WAC_NETWORK_CONNECTION_AUTODNS for auto),
+** and the send and receive buffer sizes.
+** 2. Call connect() to have it connect to a host/port (the hostname will be
+** resolved if possible).
+** 3. call run() with the maximum send/recv amounts, and optionally parameters
+** so you can tell how much has been send/received. You want to do this a lot, while:
+** 4. check get_state() to check the state of the connection. The states are:
+** JNL_Connection::STATE_ERROR
+** - an error has occured on the connection. the connection has closed,
+** and you can no longer write to the socket (there still might be
+** data in the receive buffer - use recv_bytes_available()).
+** JNL_Connection::STATE_NOCONNECTION
+** - no connection has been made yet. call connect() already! :)
+** JNL_Connection::STATE_RESOLVING
+** - the connection is still waiting for a JNL_AsycnDNS to resolve the
+** host.
+** JNL_Connection::STATE_CONNECTING
+** - the asynchronous call to connect() is still running.
+** JNL_Connection::STATE_CONNECTED
+** - the connection has connected, all is well.
+** JNL_Connection::STATE_CLOSING
+** - the connection is closing. This happens after a call to close,
+** without the quick parameter set. This means that the connection
+** will close once the data in the send buffer is sent (data could
+** still be being received when it would be closed). After it is
+** closed, the state will transition to:
+** JNL_Connection::STATE_CLOSED
+** - the connection has closed, generally without error. There still
+** might be data in the receieve buffer, use recv_bytes_available().
+** 5. Use send() and send_string() to send data. You can use
+** send_bytes_in_queue() to see how much has yet to go out, or
+** send_bytes_available() to see how much you can write. If you use send()
+** or send_string() and not enough room is available, both functions will
+** return error ( < 0)
+** 6. Use recv() and recv_line() to get data. If you want to see how much data
+** there is, use recv_bytes_available() and recv_lines_available(). If you
+** call recv() and not enough data is available, recv() will return how much
+** data was actually read. See comments at the function defs.
+**
+** 7. To close, call close(1) for a quick close, or close() for a close that will
+** make the socket close after sending all the data sent.
+**
+** 8. delete ye' ol' object.
+*/
+
+#ifndef _UDPCONNECTION_H_
+#define _UDPCONNECTION_H_
+
+#include "asyncdns.h"
+#include "nu/RingBuffer.h"
+#include "jnetlib_defines.h"
+
+#define JNL_DEFAULT_BUFFER_SIZE 8192
+
+class JNL_UDPConnection : private Drainer, private Filler
+{
+public:
+ typedef enum
+ {
+ STATE_ERROR = JNL_CONNECTION_STATE_ERROR,
+ STATE_NOCONNECTION = JNL_CONNECTION_STATE_NOCONNECTION,
+ STATE_RESOLVING = JNL_CONNECTION_STATE_RESOLVING,
+ STATE_CONNECTING = JNL_CONNECTION_STATE_CONNECTING,
+ STATE_CONNECTED = JNL_CONNECTION_STATE_CONNECTED,
+ STATE_CLOSING = JNL_CONNECTION_STATE_CLOSING,
+ STATE_CLOSED = JNL_CONNECTION_STATE_CLOSED,
+ STATE_RESOLVED = JNL_CONNECTION_STATE_RESOLVED,
+ } state;
+
+ JNL_UDPConnection();
+ JNL_UDPConnection( unsigned short port, JNL_AsyncDNS *dns, int sendbufsize = JNL_DEFAULT_BUFFER_SIZE, int recvbufsize = JNL_DEFAULT_BUFFER_SIZE );
+ ~JNL_UDPConnection();
+
+ void open( JNL_AsyncDNS *dns = JNL_AUTODNS, size_t sendbufsize = JNL_DEFAULT_BUFFER_SIZE, size_t recvbufsize = JNL_DEFAULT_BUFFER_SIZE );
+ void open( int incoming_socket, JNL_AsyncDNS *dns = JNL_AUTODNS, size_t sendbufsize = JNL_DEFAULT_BUFFER_SIZE, size_t recvbufsize = JNL_DEFAULT_BUFFER_SIZE );
+
+ void setpeer( const char *hostname, int port );
+ void setpeer( sockaddr *addr, socklen_t length /* of addr */ );
+
+ void run( size_t max_send_bytes = -1, size_t max_recv_bytes = -1, size_t *bytes_sent = NULL, size_t *bytes_rcvd = NULL );
+
+ int get_state() { return m_state; }
+ const char *get_errstr() { return m_errorstr; }
+
+ void close( int quick = 0 );
+ void flush_send( void ) { send_buffer.clear(); }
+
+ size_t send_bytes_in_queue( void );
+ size_t send_bytes_available( void );
+ int send( const void *data, size_t length ); // returns -1 if not enough room
+ int send_string( const char *line ); // returns -1 if not enough room
+
+
+ size_t recv_bytes_available( void );
+ size_t recv_bytes( void *data, size_t maxlength ); // returns actual bytes read
+ unsigned int recv_int( void );
+ int recv_lines_available( void );
+ int recv_line( char *line, size_t maxlength ); // returns 0 if the line was terminated with a \r or \n, 1 if not.
+ // (i.e. if you specify maxlength=10, and the line is 12 bytes long
+ // it will return 1. or if there is no \r or \n and that's all the data
+ // the connection has.)
+ size_t peek_bytes( void *data, size_t maxlength ); // returns bytes peeked
+
+ int get_interface( sockaddr *sin, socklen_t *sin_length ); // this returns the interface the connection is on
+ short get_remote_port( void ) { return m_remote_port; } // this returns the remote port of connection
+
+ void get_last_recv_msg_addr( sockaddr **addr, socklen_t *len ) { *addr = (sockaddr *)&m_last_addr; *len = m_last_addr_len; }
+
+ void set_ttl( uint8_t new_ttl );
+
+protected:
+ uint8_t ttl;
+ SOCKET m_socket;
+ unsigned short m_remote_port;
+ RingBuffer recv_buffer;
+ RingBuffer send_buffer;
+
+ sockaddr *address;
+ socklen_t address_len;
+
+ sockaddr_storage m_last_addr;
+ socklen_t m_last_addr_len;
+ addrinfo *saddr;
+
+ char *m_host;
+
+ JNL_AsyncDNS *m_dns;
+ int m_dns_owned;
+
+ state m_state;
+ const char *m_errorstr;
+
+private:
+ void init(); // constructor helper function
+
+ // functions for RingBuffer
+ size_t Read( void *dest, size_t len );
+ size_t Write( const void *dest, size_t len );
+
+};
+
+#endif // _UDPConnection_H_
diff --git a/Src/replicant/jnetlib/util.cpp b/Src/replicant/jnetlib/util.cpp
new file mode 100644
index 000000000..3b2b5a114
--- /dev/null
+++ b/Src/replicant/jnetlib/util.cpp
@@ -0,0 +1,185 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: util.cpp - JNL implementation of basic network utilities
+** License: see jnetlib.h
+*/
+
+#include "netinc.h"
+#include "util.h"
+#include "foundation/error.h"
+#ifdef USE_SSL
+#include "sslconnection.h"
+#ifdef _WIN32
+#include <wincrypt.h>
+#endif
+#include <openssl/rand.h>
+
+#ifdef _WIN32
+static HCRYPTPROV GetKeySet()
+{
+ HCRYPTPROV hCryptProv;
+ LPCWSTR UserName = L"WinampKeyContainer"; // name of the key container
+
+ if (CryptAcquireContext(
+ &hCryptProv, // handle to the CSP
+ UserName, // container name
+ NULL, // use the default provider
+ PROV_RSA_FULL, // provider type
+ 0)) // flag values
+ {
+ return hCryptProv;
+ }
+ else if (CryptAcquireContext(
+ &hCryptProv,
+ UserName,
+ NULL,
+ PROV_RSA_FULL,
+ CRYPT_NEWKEYSET))
+ {
+ return hCryptProv;
+ }
+ else
+ return 0;
+}
+#endif
+
+static void InitSSL()
+{
+ SSL_load_error_strings();
+ SSL_library_init();
+#ifdef _WIN32
+ HCRYPTPROV hCryptProv = GetKeySet();
+ if (hCryptProv)
+ {
+ BYTE pbData[8*sizeof(unsigned long)] = {0};
+ if (CryptGenRandom(hCryptProv, 8*sizeof(unsigned long), pbData))
+ {
+ RAND_seed(pbData, 16);
+ }
+ CryptReleaseContext(hCryptProv,0);
+ }
+#endif
+// sslContext = SSL_CTX_new(SSLv23_client_method());
+// SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
+
+// SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);
+}
+static int open_ssl_initted = 0;
+#endif
+
+static int was_initted = 0;
+
+int JNL::open_socketlib()
+{
+#ifdef _WIN32
+ if (!was_initted)
+ {
+ WSADATA wsaData = {0};
+ if (WSAStartup(MAKEWORD(1, 1), &wsaData))
+ {
+ return NErr_Error;
+ }
+ }
+#endif
+#ifdef USE_SSL
+ if (!open_ssl_initted)
+ {
+ InitSSL();
+ open_ssl_initted=1;
+ }
+#endif
+ return NErr_Success;
+}
+
+void JNL::close_socketlib()
+{
+#ifdef _WIN32
+ if (was_initted)
+ {
+ WSACleanup();
+ }
+#ifdef USE_SSL
+ // TODO need to do some reference counting to free this correctly
+ //SSL_CTX_free(sslContext);
+#endif
+#endif
+}
+
+static char *jnl_strndup(const char *str, size_t n)
+{
+ char *o = (char *)calloc(n+1, sizeof(char));
+ if (!o)
+ {
+ return 0;
+ }
+ strncpy(o, str, n);
+ o[n]=0;
+ return o;
+}
+
+int JNL::parse_url(const char *url, char **prot, char **host, unsigned short *port, char **req, char **lp)
+{
+ free(*prot); *prot=0;
+ free(*host); *host = 0;
+ free(*req); *req = 0;
+ free(*lp); *lp = 0;
+ *port = 0;
+
+ const char *p;
+ const char *protocol = strstr(url, "://");
+ if (protocol)
+ {
+ *prot = jnl_strndup(url, protocol-url);
+ p = protocol + 3;
+ }
+ else
+ {
+ p = url;
+ }
+
+ while (p && *p && *p == '/') p++; // skip extra /
+
+ size_t end = strcspn(p, "@/");
+
+ // check for username
+ if (p[end] == '@')
+ {
+ *lp = jnl_strndup(p, end);
+ p = p+end+1;
+ end = strcspn(p, "[:/");
+ }
+
+ if (p[0] == '[') // IPv6 style address
+ {
+ p++;
+ const char *ipv6_end = strchr(p, ']');
+ if (!ipv6_end)
+ return NErr_Malformed;
+
+ *host = jnl_strndup(p, ipv6_end-p);
+ p = ipv6_end+1;
+ }
+ else
+ {
+ end = strcspn(p, ":/");
+ *host = jnl_strndup(p, end);
+ p += end;
+ }
+
+ // is there a port number?
+ if (p[0] == ':')
+ {
+ char *new_end;
+ *port = (unsigned short)strtoul(p+1, &new_end, 10);
+ p = new_end;
+ }
+
+ if (p[0])
+ {
+ *req = _strdup(p);
+ }
+
+ return NErr_Success;
+}
diff --git a/Src/replicant/jnetlib/util.h b/Src/replicant/jnetlib/util.h
new file mode 100644
index 000000000..fa1cd0050
--- /dev/null
+++ b/Src/replicant/jnetlib/util.h
@@ -0,0 +1,40 @@
+/*
+** JNetLib
+** Copyright (C) 2000-2007 Nullsoft, Inc.
+** Author: Justin Frankel
+** File: util.h - JNL interface for basic network utilities
+** License: see jnetlib.h
+**
+** routines you may be interested in:
+** JNL::open_socketlib();
+** opens the socket library. Call this once before using any network
+** code. If you create a new thread, call this again. Only really an
+** issue for Win32 support, but use it anyway for portability/
+**
+** JNL::close_Socketlib();
+** closes the socketlib. Call this when you're done with the network,
+** after all your JNetLib objects have been destroyed.
+**
+** unsigned long JNL::ipstr_to_addr(const char *cp);
+** gives you the integer representation of a ip address in dotted
+** decimal form.
+**
+** JNL::addr_to_ipstr(unsigned long addr, char *host, int maxhostlen);
+** gives you the dotted decimal notation of an integer ip address.
+**
+*/
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+class JNL
+{
+public:
+ static int open_socketlib();
+ static void close_socketlib();
+ static unsigned long ipstr_to_addr( const char *cp );
+ static void addr_to_ipstr( unsigned long addr, char *host, int maxhostlen );
+ static int parse_url( const char *url, char **prot, char **host, unsigned short *port, char **req, char **lp );
+};
+
+#endif //_UTIL_H_
diff --git a/Src/replicant/jnetlib/version.rc2 b/Src/replicant/jnetlib/version.rc2
new file mode 100644
index 000000000..a5a7ce27d
--- /dev/null
+++ b/Src/replicant/jnetlib/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION WINAMP_PRODUCTVER
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Support Library"
+ VALUE "FileVersion", STR_WINAMP_PRODUCTVER
+ VALUE "InternalName", "jnetlib.dll"
+ VALUE "LegalCopyright", "Copyright © 2012-2014 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "jnetlib.dll"
+ VALUE "ProductName", "Winamp Networking Service"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/replicant/metadata/MetadataKeys.h b/Src/replicant/metadata/MetadataKeys.h
new file mode 100644
index 000000000..c61135281
--- /dev/null
+++ b/Src/replicant/metadata/MetadataKeys.h
@@ -0,0 +1,50 @@
+#pragma once
+
+namespace MetadataKeys
+{
+ const int EXTENDED_KEYS_OFFSET = 1000; // The offset to where the extended id's start
+
+ enum
+ {
+ UNKNOWN=-1,
+ ARTIST=0,
+ ALBUM_ARTIST=1,
+ ALBUM=2,
+ TITLE=3,
+ URI=4,
+ GENRE=5,
+ YEAR=6,
+ TRACK=7, // in ifc_metadata::GetField, this might return something like "2/12" for track 2 out of 12. in ifc_metadata::GetInteger, you will just get the track number (use TRACKS for total)
+ DISC=8, // in ifc_metadata::GetField, this might return something like "1/2" for disc 1 out of 2. in ifc_metadata::GetInteger, you will just get the disc number (use DISCS for total)
+ BITRATE=9,
+ COMPOSER=10,
+ PUBLISHER=11,
+ BPM=12,
+ COMMENT=13,
+ DISCS=14, // only valid for use in ifc_metadata::GetInteger
+ FILE_SIZE=15,
+ FILE_TIME=16,
+ LENGTH=17,
+ PLAY_COUNT=18,
+ RATING=19,
+ SERVER=20,
+ MIME_TYPE=21,
+ TRACK_GAIN=22,
+ TRACK_PEAK=23,
+ ALBUM_GAIN=24,
+ ALBUM_PEAK=25,
+ TRACKS=26, // only valid for use in ifc_metadata::GetInteger
+ PREGAP=27,
+ POSTGAP=28,
+ STAT=29,
+ CATEGORY=30,
+ DIRECTOR=31,
+ PRODUCER=32,
+ LAST_PLAY=33,
+ LAST_UPDATE=34,
+ ADDED=35, // date added
+ CLOUD=36, // used by pmp_cloud for the 'all sources' view
+ METAHASH=37, // used by pmp_cloud for the 'all sources' view
+ NUM_OF_METADATA_KEYS,
+ };
+}
diff --git a/Src/replicant/metadata/api_artwork.h b/Src/replicant/metadata/api_artwork.h
new file mode 100644
index 000000000..b4ee6be57
--- /dev/null
+++ b/Src/replicant/metadata/api_artwork.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "service/types.h"
+#include "nx/nx.h"
+#include "foundation/foundation.h"
+#include "metadata/types.h"
+
+// {60DB7F78-0238-4DA7-9260-87E60D30FAC4}
+static const GUID artwork_api_guid =
+{ 0x60db7f78, 0x238, 0x4da7, { 0x92, 0x60, 0x87, 0xe6, 0xd, 0x30, 0xfa, 0xc4 } };
+
+// ----------------------------------------------------------------------------
+class NOVTABLE api_artwork : public Wasabi2::Dispatchable
+{
+protected:
+ api_artwork() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_artwork() {}
+public:
+ static GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+ static GUID GetServiceGUID() { return artwork_api_guid; }
+
+ /* returns the data for the first piece of artwork found
+ pass NULL for any of the values that you don't care about */
+ int GetArtwork(nx_uri_t filename, unsigned int field, artwork_t *artwork, data_flags_t flags=DATA_FLAG_ALL, nx_time_unix_64_t *file_modified=0)
+ {
+ return Artwork_GetArtwork(filename, field, artwork, flags, file_modified);
+ }
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+protected:
+ virtual int WASABICALL Artwork_GetArtwork(nx_uri_t filename, unsigned int field, artwork_t *artwork, data_flags_t flags, nx_time_unix_64_t *file_modified)=0;
+};
+
diff --git a/Src/replicant/metadata/api_metadata.h b/Src/replicant/metadata/api_metadata.h
new file mode 100644
index 000000000..18a9de85c
--- /dev/null
+++ b/Src/replicant/metadata/api_metadata.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "service/types.h"
+
+#include "nx/nxuri.h"
+#include "metadata/ifc_metadata.h"
+#include "metadata/ifc_metadata_editor.h"
+#include "metadata/MetadataKeys.h"
+
+// {63149C84-08DC-4EA0-9351-2B0CB263FE55}
+static const GUID metadataApiServiceGuid =
+{ 0x63149c84, 0x8dc, 0x4ea0, { 0x93, 0x51, 0x2b, 0xc, 0xb2, 0x63, 0xfe, 0x55 } };
+
+
+// ----------------------------------------------------------------------------
+class NOVTABLE api_metadata: public Wasabi2::Dispatchable
+{
+protected:
+ api_metadata() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_metadata() {}
+public:
+ static GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+ static GUID GetServiceGUID() { return metadataApiServiceGuid; }
+
+ ns_error_t RegisterField(nx_string_t field_name, int *field) { return Metadata_RegisterField(field_name, field); } // Register for a non standard metadata field name
+ ns_error_t GetFieldKey(nx_string_t field_name, int *field) { return Metadata_GetFieldKey(field_name, field); } // Return the integer key of the current field
+ ns_error_t GetFieldName(int field_key, nx_string_t *name) { return Metadata_GetFieldName(field_key, name); } // Return the field name when provided with the key
+ ns_error_t GetGenre(uint8_t genre_id, nx_string_t *genre) { return Metadata_GetGenre(genre_id, genre); }
+ ns_error_t GetGenreID(nx_string_t genre, uint8_t *genre_id) { return Metadata_GetGenreID(genre, genre_id); }
+ ns_error_t SupportedFilename(nx_uri_t filename) { return Metadata_SupportedFilename(filename); }
+ ns_error_t CreateMetadata(ifc_metadata **out_metadata, nx_uri_t filename) { return Metadata_CreateMetadata(out_metadata, filename); }
+ ns_error_t CreateMetadataEditor(ifc_metadata_editor **out_metadata, nx_uri_t filename) { return Metadata_CreateMetadataEditor(out_metadata, filename); }
+ enum
+ {
+ DISPATCHABLE_VERSION, // ToDo: Does this need to be set here?
+ };
+protected:
+ virtual ns_error_t WASABICALL Metadata_RegisterField(nx_string_t field_name, int *field)=0;
+ virtual ns_error_t WASABICALL Metadata_GetFieldKey(nx_string_t field_name, int *field)=0;
+ virtual ns_error_t WASABICALL Metadata_GetFieldName(int field_key, nx_string_t *name)=0;
+ virtual ns_error_t WASABICALL Metadata_GetGenre(uint8_t genre_id, nx_string_t *genre)=0;
+ virtual ns_error_t WASABICALL Metadata_GetGenreID(nx_string_t genre, uint8_t *genre_id)=0;
+ virtual ns_error_t WASABICALL Metadata_SupportedFilename(nx_uri_t filename)=0;
+ virtual ns_error_t WASABICALL Metadata_CreateMetadata(ifc_metadata **out_metadata, nx_uri_t filename)=0;
+ virtual ns_error_t WASABICALL Metadata_CreateMetadataEditor(ifc_metadata_editor **out_metadata, nx_uri_t filename)=0;
+};
+
diff --git a/Src/replicant/metadata/genres.h b/Src/replicant/metadata/genres.h
new file mode 100644
index 000000000..7609a1c97
--- /dev/null
+++ b/Src/replicant/metadata/genres.h
@@ -0,0 +1,41 @@
+#pragma once
+
+static const char* metadata_genre_list[] = {
+ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
+ "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
+ "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
+ "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
+ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
+ "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
+ "Game", "Sound Clip", "Gospel", "Noise", "Alt Rock", "Bass",
+ "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
+ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
+ "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
+ "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
+ "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
+ "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
+ "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing",
+ "Fast-Fusion", "Bebop", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
+ "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
+ "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
+ "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
+ "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
+ "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
+ "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
+ "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
+ "Indie", "BritPop", "Afro-Punk", "Polsk Punk", "Beat",
+ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian",
+ "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
+ "SynthPop", "Abstract", "Art Rock", "Baroque", "Bhangra", "Big Beat", "Breakbeat", "Chillout", "Downtempo", "Dub", "EBM", "Eclectic", "Electro",
+ "Electroclash", "Emo", "Experimental", "Garage", "Global", "IDM", "Illbient", "Industro-Goth", "Jam Band", "Krautrock", "Leftfield", "Lounge",
+ "Math Rock", "New Romantic", "Nu-Breakz", "Post-Punk", "Post-Rock", "Psytrance", "Shoegaze", "Space Rock", "Trop Rock", "World Music", "Neoclassical",
+ "Audiobook", "Audio Theatre", "Neue Deutsche Welle", "Podcast", "Indie Rock", "G-Funk", "Dubstep", "Garage Rock", "Psybient", "Glam Rock", "Dream Pop",
+ "Merseybeat", "K-Pop", "Chiptune", "Grime", "Grindcore", "Indietronic", "Indietronica", "Jazz Rock", "Jazz Fusion", "Post-Punk Revival", "Electronica",
+ "Psychill", "Ethnotronic", "Americana", "Ambient Dub", "Digital Dub", "Chillwave", "Stoner Rock", "Slowcore", "Softcore", "Flamenco", "Hi-NRG", "Ethereal",
+ "Drone", "Doom Metal", "Doom Jazz", "Mainstream", "Glitch", "Balearic", "Modern Classical", "Mod", "Contemporary Classical", "Psybreaks", "Psystep",
+ "Psydub", "Chillstep", "Berlin School", "Future Jazz", "Djent", "Musique Concrète", "Electroacoustic", "Folktronica", "Texas Country", "Red Dirt",
+ "Arabic", "Asian", "Bachata", "Bollywood", "Cajun", "Calypso", "Creole", "Darkstep", "Jewish", "Reggaeton", "Smooth Jazz", "Soca", "Spiritual",
+ "Turntablism", "Zouk","Neofolk", "Nu Jazz", "Psychobilly", "Rockabilly", "Schlager & Volksmusik",
+};
+
+static const size_t metadata_genre_list_size = sizeof(metadata_genre_list)/sizeof(*metadata_genre_list);
diff --git a/Src/replicant/metadata/ifc_metadata.h b/Src/replicant/metadata/ifc_metadata.h
new file mode 100644
index 000000000..039d69b6b
--- /dev/null
+++ b/Src/replicant/metadata/ifc_metadata.h
@@ -0,0 +1,65 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxstring.h"
+#include "nx/nxdata.h"
+#include "foundation/error.h"
+#include "metadata/MetadataKeys.h"
+#include "metadata/types.h"
+
+class NOVTABLE ifc_metadata : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_metadata() : Dispatchable(DISPATCHABLE_VERSION) {}
+public:
+ virtual ~ifc_metadata() {}
+
+ enum
+ {
+ INDEX_DEFAULT = -1,
+ };
+
+ /* Fields */
+ ns_error_t GetField(int field, unsigned int index, nx_string_t *value) { return Metadata_GetField(field, index, value); }
+ ns_error_t GetInteger(int field, unsigned int index, int64_t *value) { return Metadata_GetInteger(field, index, value); }
+ ns_error_t GetReal(int field, unsigned int index, double *value) { return Metadata_GetReal(field, index, value); }
+
+ ns_error_t SetField(int field, unsigned int index, nx_string_t value) { return Metadata_SetField(field, index, value); }
+ ns_error_t SetInteger(int field, unsigned int index, int64_t value) { return Metadata_SetInteger(field, index, value); }
+ ns_error_t SetReal(int field, unsigned int index, double value) { return Metadata_SetReal(field, index, value); }
+
+ /* Art */
+ ns_error_t GetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags=DATA_FLAG_ALL) { return Metadata_GetArtwork(field, index, artwork, flags); }
+
+ /* Binary Data */
+ ns_error_t GetBinary(int field, unsigned int index, nx_data_t *data) { return Metadata_GetBinary(field, index, data); }
+
+ /* sub-tracks */
+ ns_error_t GetMetadata(int field, unsigned int index, ifc_metadata **metadata) { return Metadata_GetMetadata(field, index, metadata); }
+
+ ns_error_t Serialize(nx_data_t *data) { return Metadata_Serialize(data); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual ns_error_t WASABICALL Metadata_GetField(int field, unsigned int index, nx_string_t *value)=0;
+ virtual ns_error_t WASABICALL Metadata_GetInteger(int field, unsigned int index, int64_t *value)=0;
+ virtual ns_error_t WASABICALL Metadata_GetReal(int field, unsigned int index, double *value)=0;
+
+ virtual ns_error_t WASABICALL Metadata_SetField(int field, unsigned int index, nx_string_t value){ return NErr_NotImplemented; };
+ virtual ns_error_t WASABICALL Metadata_SetInteger(int field, unsigned int index, int64_t value){ return NErr_NotImplemented; };
+ virtual ns_error_t WASABICALL Metadata_SetReal(int field, unsigned int index, double value){ return NErr_NotImplemented; };
+
+ /* notes:
+ passing NULL for any of the pointers (most notably, data) indicates that it's not needed
+ width and height aren't guaranteed to be actually accurate. they will be set only if it was marked in the file, otherwise they will be set to 0
+ mime_type might also come back as 0 if it wasn't stored in the metadata. and again, mime_type isn't guaranteed to be accurate
+ for type, MetadataKeys::ALBUM is for album art. use Metadata::UNKNOWN for unknown types, such as id3v2 tags with Picture types marked as "other" ($00)
+ index should be as stored in the file, do not re-arrange. */
+ virtual ns_error_t WASABICALL Metadata_GetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags)=0;
+ virtual ns_error_t WASABICALL Metadata_GetBinary(int field, unsigned int index, nx_data_t *data)=0;
+ virtual ns_error_t WASABICALL Metadata_GetMetadata(int field, unsigned int index, ifc_metadata **metadata) { return NErr_NotImplemented; }
+
+ virtual ns_error_t WASABICALL Metadata_Serialize(nx_data_t *data) { return NErr_NotImplemented; }
+};
diff --git a/Src/replicant/metadata/ifc_metadata_editor.h b/Src/replicant/metadata/ifc_metadata_editor.h
new file mode 100644
index 000000000..311e0ed3e
--- /dev/null
+++ b/Src/replicant/metadata/ifc_metadata_editor.h
@@ -0,0 +1,43 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "nx/nxstring.h"
+#include "foundation/error.h"
+#include "metadata/MetadataKeys.h"
+#include "nx/nxdata.h"
+#include "metadata/types.h"
+class NOVTABLE ifc_metadata_editor : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_metadata_editor() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_metadata_editor() {}
+public:
+ enum
+ {
+ INDEX_DEFAULT = -1,
+ };
+
+ int Save() { return MetadataEditor_Save(); }
+
+ /* Fields */
+ int SetField(int field, unsigned int index, nx_string_t value) { return MetadataEditor_SetField(field, index, value); }
+ int SetInteger(int field, unsigned int index, int64_t value) { return MetadataEditor_SetInteger(field, index, value); }
+ int SetReal(int field, unsigned int index, double value) { return MetadataEditor_SetReal(field, index, value); }
+
+ /* Art */
+ int SetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags=DATA_FLAG_ALL) { return MetadataEditor_SetArtwork(field, index, data, flags); }
+
+ /* Binary Data */
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual int WASABICALL MetadataEditor_Save()=0;
+
+ virtual int WASABICALL MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)=0;
+ virtual int WASABICALL MetadataEditor_SetInteger(int field, unsigned int index, int64_t value)=0;
+ virtual int WASABICALL MetadataEditor_SetReal(int field, unsigned int index, double value)=0;
+
+ virtual int WASABICALL MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags)=0;
+};
diff --git a/Src/replicant/metadata/metadata.h b/Src/replicant/metadata/metadata.h
new file mode 100644
index 000000000..dc137c553
--- /dev/null
+++ b/Src/replicant/metadata/metadata.h
@@ -0,0 +1,7 @@
+#pragma once
+/* convenience header that includes everything */
+#include "api_metadata.h"
+#include "ifc_metadata.h"
+#include "ifc_metadata_editor.h"
+#include "MetadataKeys.h"
+#include "svc_metadata.h"
diff --git a/Src/replicant/metadata/metadata.xcodeproj/project.pbxproj b/Src/replicant/metadata/metadata.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..91b1f081f
--- /dev/null
+++ b/Src/replicant/metadata/metadata.xcodeproj/project.pbxproj
@@ -0,0 +1,268 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 00DC1311150722D0007F9D73 /* metadata */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 00DC1312150722D0007F9D73 /* Build configuration list for PBXAggregateTarget "metadata" */;
+ buildPhases = (
+ 00DC1315150722F0007F9D73 /* CopyFiles */,
+ 00B73372151B65EF00A8251C /* Install Public Headers */,
+ );
+ dependencies = (
+ 00B73378151B665900A8251C /* PBXTargetDependency */,
+ );
+ name = metadata;
+ productName = metadata;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 00B7336E151B645C00A8251C /* metadata.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00B7336D151B645C00A8251C /* metadata.h */; };
+ 00B73371151B65AA00A8251C /* genres.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00B73370151B65AA00A8251C /* genres.h */; };
+ 00D69B1A15BDD5AC0026710F /* types.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00D69B1915BDD5AC0026710F /* types.h */; };
+ 00DC131615072301007F9D73 /* api_metadata.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0088402315054AF300625F51 /* api_metadata.h */; };
+ 00DC131715072301007F9D73 /* ifc_metadata_editor.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0088402415054AF300625F51 /* ifc_metadata_editor.h */; };
+ 00DC131815072301007F9D73 /* ifc_metadata.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0088402515054AF300625F51 /* ifc_metadata.h */; };
+ 00DC131915072301007F9D73 /* MetadataKeys.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0088402615054AF300625F51 /* MetadataKeys.h */; };
+ 00DC131A15072301007F9D73 /* svc_metadata.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0088402715054AF300625F51 /* svc_metadata.h */; };
+ 00F535871553671D0069AC35 /* api_artwork.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00F535861553671D0069AC35 /* api_artwork.h */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 00B73377151B665900A8251C /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0088401015054ABD00625F51 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00B73373151B662700A8251C;
+ remoteInfo = "metadata-cleanup";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 00DC1315150722F0007F9D73 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)";
+ dstSubfolderSpec = 16;
+ files = (
+ 00DC131615072301007F9D73 /* api_metadata.h in CopyFiles */,
+ 00DC131715072301007F9D73 /* ifc_metadata_editor.h in CopyFiles */,
+ 00DC131815072301007F9D73 /* ifc_metadata.h in CopyFiles */,
+ 00DC131915072301007F9D73 /* MetadataKeys.h in CopyFiles */,
+ 00DC131A15072301007F9D73 /* svc_metadata.h in CopyFiles */,
+ 00B7336E151B645C00A8251C /* metadata.h in CopyFiles */,
+ 00B73371151B65AA00A8251C /* genres.h in CopyFiles */,
+ 00F535871553671D0069AC35 /* api_artwork.h in CopyFiles */,
+ 00D69B1A15BDD5AC0026710F /* types.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 0088402315054AF300625F51 /* api_metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = api_metadata.h; sourceTree = "<group>"; };
+ 0088402415054AF300625F51 /* ifc_metadata_editor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ifc_metadata_editor.h; sourceTree = "<group>"; };
+ 0088402515054AF300625F51 /* ifc_metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ifc_metadata.h; sourceTree = "<group>"; };
+ 0088402615054AF300625F51 /* MetadataKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MetadataKeys.h; sourceTree = "<group>"; };
+ 0088402715054AF300625F51 /* svc_metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = svc_metadata.h; sourceTree = "<group>"; };
+ 00B7336D151B645C00A8251C /* metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadata.h; sourceTree = "<group>"; };
+ 00B73370151B65AA00A8251C /* genres.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = genres.h; sourceTree = "<group>"; };
+ 00D69B1915BDD5AC0026710F /* types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; };
+ 00F535861553671D0069AC35 /* api_artwork.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = api_artwork.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXGroup section */
+ 0088400E15054ABD00625F51 = {
+ isa = PBXGroup;
+ children = (
+ 00F535861553671D0069AC35 /* api_artwork.h */,
+ 0088402315054AF300625F51 /* api_metadata.h */,
+ 0088402415054AF300625F51 /* ifc_metadata_editor.h */,
+ 0088402515054AF300625F51 /* ifc_metadata.h */,
+ 0088402615054AF300625F51 /* MetadataKeys.h */,
+ 0088402715054AF300625F51 /* svc_metadata.h */,
+ 00B7336D151B645C00A8251C /* metadata.h */,
+ 00B73370151B65AA00A8251C /* genres.h */,
+ 00D69B1915BDD5AC0026710F /* types.h */,
+ );
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+ 00B73373151B662700A8251C /* metadata-cleanup */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(NSBUILD_TOOLS_BIN_DIR)/cleanbuild --xcode-mode \"$(PUBLIC_HEADERS_DIR)\"";
+ buildConfigurationList = 00B73374151B662700A8251C /* Build configuration list for PBXLegacyTarget "metadata-cleanup" */;
+ buildPhases = (
+ );
+ buildToolPath = /bin/sh;
+ buildWorkingDirectory = "";
+ dependencies = (
+ );
+ name = "metadata-cleanup";
+ passBuildSettingsInEnvironment = 1;
+ productName = "metadata-cleanup";
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXProject section */
+ 0088401015054ABD00625F51 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0430;
+ ORGANIZATIONNAME = "Nullsoft, Inc.";
+ };
+ buildConfigurationList = 0088401315054ABD00625F51 /* Build configuration list for PBXProject "metadata" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 0088400E15054ABD00625F51;
+ productRefGroup = 0088400E15054ABD00625F51;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 00DC1311150722D0007F9D73 /* metadata */,
+ 00B73373151B662700A8251C /* metadata-cleanup */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00B73372151B65EF00A8251C /* Install Public Headers */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ name = "Install Public Headers";
+ outputPaths = (
+ "$(DSTROOT)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "INSTALLTOOL=\"$NSBUILD_TOOLS_BIN_DIR/installtool\"\n$INSTALLTOOL --headers-only \\\n \"$SCRIPT_INPUT_FILE_0/\" \\\n \"$SCRIPT_OUTPUT_FILE_0\"";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 00B73378151B665900A8251C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00B73373151B662700A8251C /* metadata-cleanup */;
+ targetProxy = 00B73377151B665900A8251C /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 0088401B15054ABD00625F51 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH_AMD64 = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx-amd64";
+ PUBLIC_HEADERS_FOLDER_PATH_X86 = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx-x86";
+ SDKROOT = macosx;
+ SHARE_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Debug;
+ };
+ 0088401C15054ABD00625F51 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH_AMD64 = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx-amd64";
+ PUBLIC_HEADERS_FOLDER_PATH_X86 = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx-x86";
+ SDKROOT = macosx;
+ SHARE_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Release;
+ };
+ 00B73375151B662700A8251C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Debug;
+ };
+ 00B73376151B662700A8251C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Release;
+ };
+ 00DC1313150722D0007F9D73 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 00DC1314150722D0007F9D73 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 0088401315054ABD00625F51 /* Build configuration list for PBXProject "metadata" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0088401B15054ABD00625F51 /* Debug */,
+ 0088401C15054ABD00625F51 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 00B73374151B662700A8251C /* Build configuration list for PBXLegacyTarget "metadata-cleanup" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00B73375151B662700A8251C /* Debug */,
+ 00B73376151B662700A8251C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 00DC1312150722D0007F9D73 /* Build configuration list for PBXAggregateTarget "metadata" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00DC1313150722D0007F9D73 /* Debug */,
+ 00DC1314150722D0007F9D73 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0088401015054ABD00625F51 /* Project object */;
+}
diff --git a/Src/replicant/metadata/metadata.xcodeproj/xcshareddata/xcschemes/metadata.xcscheme b/Src/replicant/metadata/metadata.xcodeproj/xcshareddata/xcschemes/metadata.xcscheme
new file mode 100644
index 000000000..948658166
--- /dev/null
+++ b/Src/replicant/metadata/metadata.xcodeproj/xcshareddata/xcschemes/metadata.xcscheme
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0460"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "00DC1311150722D0007F9D73"
+ BuildableName = "metadata"
+ BlueprintName = "metadata"
+ ReferencedContainer = "container:metadata.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Src/replicant/metadata/svc_metadata.h b/Src/replicant/metadata/svc_metadata.h
new file mode 100644
index 000000000..d3a0dace3
--- /dev/null
+++ b/Src/replicant/metadata/svc_metadata.h
@@ -0,0 +1,65 @@
+#pragma once
+#include "foundation/foundation.h"
+#include "ifc_metadata.h"
+#include "ifc_metadata_editor.h"
+#include "nx/nxuri.h"
+
+// {7DBF780E-78B6-436C-A188-864AF6859D87}
+static const GUID metadata_service_type_guid =
+{ 0x7dbf780e, 0x78b6, 0x436c, { 0xa1, 0x88, 0x86, 0x4a, 0xf6, 0x85, 0x9d, 0x87 } };
+
+class svc_metadata : public Wasabi2::Dispatchable
+{
+protected:
+ svc_metadata() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_metadata() {}
+public:
+ static GUID GetServiceType() { return 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 MetadataService_EnumerateExtensions(index, extension); }
+
+ int CreateMetadata(nx_uri_t filename, ifc_metadata **metadata) {return MetadataService_CreateMetadata(filename, metadata); }
+ int CreateMetadataEditor(nx_uri_t filename, ifc_metadata_editor **metadata) { return MetadataService_CreateMetadataEditor(filename, metadata); }
+
+ int DeserializeMetadata(nx_data_t data, ifc_metadata **metadata) { return MetadataService_DeserializeMetadata(data, metadata); }
+
+ int CreateMetadata(unsigned int pass, nx_uri_t filename, ifc_metadata **metadata)
+ {
+ if (dispatchable_version == 0)
+ {
+ if (pass == 0)
+ return MetadataService_CreateMetadata(filename, metadata);
+ else
+ return NErr_False;
+ }
+ else
+ return MetadataService_CreateMetadata(pass, filename, metadata);
+ }
+ int CreateMetadataEditor(unsigned int pass, nx_uri_t filename, ifc_metadata_editor **metadata)
+ {
+ if (dispatchable_version == 0)
+ {
+ if (pass == 0)
+ return MetadataService_CreateMetadataEditor(filename, metadata);
+ else
+ return NErr_False;
+ }
+ else
+ return MetadataService_CreateMetadataEditor(pass, filename, metadata);
+ }
+ enum
+ {
+ DISPATCHABLE_VERSION=1,
+ };
+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 MetadataService_EnumerateExtensions(unsigned int index, nx_string_t *extension)=0;
+
+ /* these two no longer have to be implemented */
+ virtual int WASABICALL MetadataService_CreateMetadata(nx_uri_t filename, ifc_metadata **metadata) { return MetadataService_CreateMetadata(0, filename, metadata); }
+ virtual int WASABICALL MetadataService_CreateMetadataEditor(nx_uri_t filename, ifc_metadata_editor **metadata) { return MetadataService_CreateMetadataEditor(0, filename, metadata); }
+
+ virtual int WASABICALL MetadataService_DeserializeMetadata(nx_data_t data, ifc_metadata **metadata) { return NErr_NotImplemented; }
+ virtual int WASABICALL MetadataService_CreateMetadata(unsigned int pass, nx_uri_t filename, ifc_metadata **metadata)=0;
+ virtual int WASABICALL MetadataService_CreateMetadataEditor(unsigned int pass, nx_uri_t filename, ifc_metadata_editor **metadata)=0;
+};
diff --git a/Src/replicant/metadata/types.h b/Src/replicant/metadata/types.h
new file mode 100644
index 000000000..fe26eb716
--- /dev/null
+++ b/Src/replicant/metadata/types.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "foundation/types.h"
+#include "nx/nxdata.h"
+
+typedef int data_flags_t;
+enum
+{
+ DATA_FLAG_NONE=0,
+ DATA_FLAG_DATA=(1<<0),
+ DATA_FLAG_SOURCE_INFORMATION=(1<<1),
+ DATA_FLAG_MIME=(1<<2),
+ DATA_FLAG_DESCRIPTION=(1<<3),
+ DATA_FLAG_ALL=DATA_FLAG_DATA|DATA_FLAG_SOURCE_INFORMATION|DATA_FLAG_MIME|DATA_FLAG_DESCRIPTION,
+};
+
+class artwork_t
+{
+public:
+ artwork_t()
+ {
+ data=0;
+ width=0;
+ height=0;
+ }
+
+ ~artwork_t()
+ {
+ NXDataRelease(data);
+ }
+
+ nx_data_t data;
+ uint32_t width;
+ uint32_t height;
+};
diff --git a/Src/replicant/mp4/MP4Decoder.h b/Src/replicant/mp4/MP4Decoder.h
new file mode 100644
index 000000000..f7791cb4d
--- /dev/null
+++ b/Src/replicant/mp4/MP4Decoder.h
@@ -0,0 +1,24 @@
+//
+// MP4Decoder.h
+//
+// Created by Ben Allison on 1/17/12.
+// Copyright (c) 2012 Nullsoft, Inc. All rights reserved.
+//
+#pragma once
+
+#include "file/svc_filedecode.h"
+#include "nswasabi/ServiceName.h"
+
+// {7EDB2571-AA99-4DF0-8247-A7846B338B04}
+static const GUID mp4_file_decoder_guid =
+{ 0x7edb2571, 0xaa99, 0x4df0, { 0x82, 0x47, 0xa7, 0x84, 0x6b, 0x33, 0x8b, 0x4 } };
+
+class MP4Decoder : public svc_filedecode
+{
+public:
+ WASABI_SERVICE_NAME("MP4 File Decoder");
+ WASABI_SERVICE_GUID(mp4_file_decoder_guid);
+
+private:
+ 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);
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4DecoderCallback.h b/Src/replicant/mp4/MP4DecoderCallback.h
new file mode 100644
index 000000000..7ef992dc6
--- /dev/null
+++ b/Src/replicant/mp4/MP4DecoderCallback.h
@@ -0,0 +1,35 @@
+//
+// FLACDecoderPull.h
+// flac
+//
+// Created by Ben Allison on 1/12/12.
+// Copyright (c) 2012 Nullsoft, Inc. All rights reserved.
+//
+#pragma once
+#include "audio/ifc_audio_decoder_callback.h"
+#include "mp4/ifc_mp4audiodecoder.h"
+#include "mp4.h"
+#include "audio/parameters.h"
+#include "MP4FileObject.h"
+#include "nswasabi/MetadataChain.h"
+
+class MP4DecoderCallback : public ifc_audio_decoder_callback
+{
+public:
+ MP4DecoderCallback();
+ ~MP4DecoderCallback();
+ int Initialize(MP4FileHandle mp4_file, ifc_mp4audiodecoder *decoder, int flags, nsaudio::Parameters *parameters, MetadataChain<MP4FileObject> *mp4_file_object);
+
+private:
+ int WASABICALL AudioDecoderCallback_GetMetadata(ifc_metadata **metadata);
+ int WASABICALL AudioDecoderCallback_Decode(ifc_audio_decoder_callback::callback *callback);
+ int WASABICALL AudioDecoderCallback_DecodeStep(ifc_audio_decoder_callback::callback *callback);
+ int WASABICALL AudioDecoderCallback_GetFrameSize(size_t *frame_size);
+ MP4FileHandle mp4_file;
+ ifc_mp4audiodecoder *audio_decoder;
+ MetadataChain<MP4FileObject> *mp4_file_object;
+ int flags;
+ size_t pregap;
+ size_t frame_size;
+ unsigned int channels;
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4FileObject.h b/Src/replicant/mp4/MP4FileObject.h
new file mode 100644
index 000000000..dfc5a934d
--- /dev/null
+++ b/Src/replicant/mp4/MP4FileObject.h
@@ -0,0 +1,33 @@
+#pragma once
+#include "ifc_mp4file.h"
+#include "MP4MetadataBase.h"
+#include "mp4.h"
+
+class MP4FileObject : public ifc_mp4file, public MP4MetadataBase
+{
+public:
+ MP4FileObject();
+ ~MP4FileObject();
+ void Initialize(nx_uri_t filename, MP4FileHandle file_handle);
+
+private:
+ MP4FileHandle file_handle;
+
+private:
+ int WASABICALL MP4File_Free(void *buffer);
+
+ int WASABICALL MP4File_Track_GetESConfiguration(TrackID track_number, uint8_t **buffer, uint32_t *buffer_size);
+ int WASABICALL MP4File_Track_GetMaxSampleSize(TrackID track_number, uint32_t *max_sample_size);
+ int WASABICALL MP4File_Track_ConvertFromTimestamp(TrackID track_number, Timestamp timestamp, double *seconds);
+ int WASABICALL MP4File_Track_ConvertToDuration(TrackID track_number, double seconds, Duration *duration);
+ int WASABICALL MP4File_Track_GetMediaDataName(TrackID track_number, const char **name);
+ int WASABICALL MP4File_Track_GetESDSObjectTypeID(TrackID track_number, uint8_t *type);
+ int WASABICALL MP4File_Track_GetAudioMPEG4Type(TrackID track_number, uint8_t *type);
+ int WASABICALL MP4File_Track_GetBytesProperty(TrackID track_number, const char *property_name, uint8_t **buffer, uint32_t *buffer_size);
+
+ int WASABICALL MP4File_Metadata_iTunes_FindFreeform(const char *name, const char *mean, metadata_itunes_atom_t *atom);
+ int WASABICALL MP4File_Metadata_iTunes_GetBinary(metadata_itunes_atom_t atom, const uint8_t **value, size_t *value_length);
+
+ int WASABICALL MP4File_Sample_Read(TrackID track_number, SampleID sample_number, uint8_t **bytes, uint32_t *bytes_length, Timestamp *start_time, Duration *duration, Duration *offset, int *is_sync);
+ int WASABICALL MP4File_Sample_GetFromDuration(TrackID track_number, Duration duration, SampleID *sample_id);
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4HTTP.h b/Src/replicant/mp4/MP4HTTP.h
new file mode 100644
index 000000000..5580d3f42
--- /dev/null
+++ b/Src/replicant/mp4/MP4HTTP.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "http/svc_http_demuxer.h"
+#include "http/ifc_http_demuxer.h"
+#include "nx/nxstring.h"
+#include "nswasabi/ServiceName.h"
+#include "MP4FileObject.h"
+#include "ifc_mp4audiodecoder.h"
+
+// {C67A19EF-CBDB-4BD9-9B4F-EEC5D6B05093}
+static const GUID mp4_demuxer_guid =
+{ 0xc67a19ef, 0xcbdb, 0x4bd9, { 0x9b, 0x4f, 0xee, 0xc5, 0xd6, 0xb0, 0x50, 0x93 } };
+
+
+class MP4HTTPService : public svc_http_demuxer
+{
+public:
+ WASABI_SERVICE_NAME("MP4 HTTP Demuxer");
+ static GUID GetServiceGUID() { return mp4_demuxer_guid; }
+
+ const char *WASABICALL HTTPDemuxerService_EnumerateAcceptedTypes(size_t i);
+ const char *WASABICALL HTTPDemuxerService_GetUserAgent();
+ void WASABICALL HTTPDemuxerService_CustomizeHTTP(jnl_http_t http);
+ NError WASABICALL HTTPDemuxerService_CreateDemuxer(nx_uri_t uri, jnl_http_t http, ifc_http_demuxer **demuxer, int pass);
+};
+
+class MP4HTTP : public ifc_http_demuxer
+{
+public:
+ MP4HTTP();
+ ~MP4HTTP();
+
+ int Initialize(nx_uri_t uri, jnl_http_t http);
+private:
+ /* ifc_http_demuxer implementation */
+ int WASABICALL HTTPDemuxer_Run(ifc_http *http_parent, ifc_player *player, ifc_playback_parameters *secondary_parameters);
+
+ /* member data */
+ jnl_http_t http;
+ nx_uri_t uri;
+ nx_file_t file;
+ MP4FileObject *mp4_file_object;
+ ifc_audioout::Parameters audio_parameters;
+ ifc_mp4audiodecoder *audio_decoder;
+};
diff --git a/Src/replicant/mp4/MP4Metadata.h b/Src/replicant/mp4/MP4Metadata.h
new file mode 100644
index 000000000..003826f76
--- /dev/null
+++ b/Src/replicant/mp4/MP4Metadata.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "MP4MetadataBase.h"
+#include "nx/nxuri.h"
+
+ class MP4Metadata : public MP4MetadataBase
+ {
+ public:
+ MP4Metadata();
+ ~MP4Metadata();
+ int Initialize(nx_uri_t filename);
+
+ private:
+ MP4FileHandle mp4_file;
+ }; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4MetadataBase.h b/Src/replicant/mp4/MP4MetadataBase.h
new file mode 100644
index 000000000..ba90692c4
--- /dev/null
+++ b/Src/replicant/mp4/MP4MetadataBase.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "metadata/ifc_metadata.h"
+#include "mp4.h"
+
+class MP4MetadataBase : public ifc_metadata
+{
+public:
+ MP4MetadataBase();
+ ~MP4MetadataBase();
+ int Initialize(nx_uri_t filename, MP4FileHandle mp4_file);
+
+ 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) { return NErr_NotImplemented; }
+private:
+ MP4FileHandle mp4_file;
+ nx_uri_t mp4_metadata_filename;
+ nx_file_stat_s file_stats;
+ static struct mime_types_t
+ {
+ nx_string_t jpeg;
+ nx_string_t png;
+ nx_string_t bmp;
+ nx_string_t gif;
+ } mime_types;
+ static int NX_ONCE_API InitMIME(nx_once_t, void *, void **);
+ static nx_string_t GetMIMEFromType(uint32_t type);
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4MetadataEditor.h b/Src/replicant/mp4/MP4MetadataEditor.h
new file mode 100644
index 000000000..038a1904e
--- /dev/null
+++ b/Src/replicant/mp4/MP4MetadataEditor.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "mp4.h"
+#include "metadata/ifc_metadata_editor.h"
+#include "nx/nxuri.h"
+
+class MP4MetadataEditor : public ifc_metadata_editor
+{
+public:
+ MP4MetadataEditor();
+ ~MP4MetadataEditor();
+ int Initialize(nx_uri_t filename);
+
+private:
+ MP4FileHandle mp4_file;
+
+ int WASABICALL MetadataEditor_Save();
+
+ 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);
+ nx_uri_t filename;
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4MetadataFile.h b/Src/replicant/mp4/MP4MetadataFile.h
new file mode 100644
index 000000000..93aedc98c
--- /dev/null
+++ b/Src/replicant/mp4/MP4MetadataFile.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "MP4MetadataBase.h"
+#include "nx/nxuri.h"
+#include "nx/nxfile.h"
+
+ class MP4MetadataFile : public MP4MetadataBase
+ {
+ public:
+ MP4MetadataFile();
+ ~MP4MetadataFile();
+ int Initialize(nx_uri_t filename, nx_file_t file);
+
+ private:
+ MP4FileHandle mp4_file;
+ }; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4MetadataService.h b/Src/replicant/mp4/MP4MetadataService.h
new file mode 100644
index 000000000..f864400c7
--- /dev/null
+++ b/Src/replicant/mp4/MP4MetadataService.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "metadata/svc_metadata.h"
+#include "file/svc_filemetadata.h"
+#include "nswasabi/ServiceName.h"
+#include "MP4MetadataFile.h"
+#include "nswasabi/MetadataChain.h"
+#include "nx/nxonce.h"
+
+// {71102FF2-D9CF-4a0e-9C83-858E80B65DD4}
+static const GUID mp4_metadata_guid =
+{ 0x71102ff2, 0xd9cf, 0x4a0e, { 0x9c, 0x83, 0x85, 0x8e, 0x80, 0xb6, 0x5d, 0xd4 } };
+
+
+class MP4MetadataService : public svc_metadata
+{
+public:
+ WASABI_SERVICE_NAME("MP4 Metadata");
+ WASABI_SERVICE_GUID(mp4_metadata_guid);
+ MP4MetadataService();
+ static nx_once_value_t mime_once;
+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);
+};
+
+// {F3AD1E12-345C-4E00-83C1-4DAB2D88BF4F}
+static const GUID mp4_file_metadata_guid =
+{ 0xf3ad1e12, 0x345c, 0x4e00, { 0x83, 0xc1, 0x4d, 0xab, 0x2d, 0x88, 0xbf, 0x4f } };
+
+class MP4FileMetadataService : public svc_filemetadata
+{
+public:
+ WASABI_SERVICE_NAME("MP4 File Metadata");
+ WASABI_SERVICE_GUID(mp4_file_metadata_guid);
+private:
+ int WASABICALL FileMetadataService_EnumerateExtensions(unsigned int index, nx_string_t *extension);
+ int WASABICALL FileMetadataService_CreateFileMetadata(ifc_metadata **file_metadata, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata);
+};
+
+class MP4FileMetadata : public MetadataChain<MP4MetadataFile>
+{
+public:
+ MP4FileMetadata();
+ ~MP4FileMetadata();
+ ns_error_t Initialize(nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata);
+
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4Playback.h b/Src/replicant/mp4/MP4Playback.h
new file mode 100644
index 000000000..ca6956efe
--- /dev/null
+++ b/Src/replicant/mp4/MP4Playback.h
@@ -0,0 +1,39 @@
+#pragma once
+#include "mp4.h"
+#include "file/ifc_fileplayback.h"
+#include "mp4/ifc_mp4audiodecoder.h"
+#include "MP4MetadataBase.h"
+#include "MP4FileObject.h"
+#include "nswasabi/MetadataChain.h"
+
+class MP4Playback : public ifc_fileplayback
+{
+public:
+ MP4Playback();
+ ~MP4Playback();
+
+ int Initialize(nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata, ifc_fileplayback_parent *parent);
+private:
+ ifc_mp4audiodecoder *audio_decoder;
+ MP4FileHandle mp4_file;
+ ifc_audioout::Parameters audio_parameters;
+ MetadataChain<MP4FileObject> *mp4_file_object;
+ bool output_opened;
+ double samples_per_second;
+ nx_file_t file;
+ ifc_fileplayback_parent *parent;
+ nx_uri_t filename;
+private:
+ int Init(nx_file_t file, ifc_metadata *parent_metadata);
+ int Configure();
+
+ void WASABICALL FilePlayback_Close();
+ ns_error_t WASABICALL FilePlayback_Seekable();
+ ns_error_t WASABICALL FilePlayback_GetMetadata(ifc_metadata **metadata);
+ ns_error_t WASABICALL FilePlayback_GetLength(double *length, ns_error_t *exact);
+ ns_error_t WASABICALL FilePlayback_GetBitrate(double *bitrate, ns_error_t *exact);
+ ns_error_t WASABICALL FilePlayback_Seek(const Agave_Seek *seek, ns_error_t *seek_error, double *new_position);
+ ns_error_t WASABICALL FilePlayback_DecodeStep();
+ ns_error_t WASABICALL FilePlayback_Interrupt(Agave_Seek *resume_information);
+ ns_error_t WASABICALL FilePlayback_Resume(Agave_Seek *resume_information, nx_file_t file, ifc_metadata *parent_metadata);
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/MP4PlaybackService.h b/Src/replicant/mp4/MP4PlaybackService.h
new file mode 100644
index 000000000..bfef5a885
--- /dev/null
+++ b/Src/replicant/mp4/MP4PlaybackService.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "file/svc_fileplayback.h"
+#include "nx/nxstring.h"
+#include "nswasabi/ServiceName.h"
+
+// {AA80A650-FC61-4011-BB17-E4EBA5C185D9}
+static const GUID mp4_file_playback_guid =
+{ 0xaa80a650, 0xfc61, 0x4011, { 0xbb, 0x17, 0xe4, 0xeb, 0xa5, 0xc1, 0x85, 0xd9 } };
+
+
+class MP4PlaybackService : public svc_fileplayback
+{
+public:
+ WASABI_SERVICE_NAME("MP4 File Playback Service");
+ WASABI_SERVICE_GUID(mp4_file_playback_guid);
+
+ 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);
+};
diff --git a/Src/replicant/mp4/MP4RawReader.h b/Src/replicant/mp4/MP4RawReader.h
new file mode 100644
index 000000000..8d1547b49
--- /dev/null
+++ b/Src/replicant/mp4/MP4RawReader.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "file/svc_filerawreader.h"
+#include "decode/ifc_raw_media_reader.h"
+#include "mp4.h"
+#include "nswasabi/ServiceName.h"
+
+// {5CBD1F27-5A63-4D8C-9297-D74518E1EF3A}
+static const GUID mp4_raw_reader_guid =
+{ 0x5cbd1f27, 0x5a63, 0x4d8c, { 0x92, 0x97, 0xd7, 0x45, 0x18, 0xe1, 0xef, 0x3a } };
+
+class MP4RawReaderService : public svc_filerawreader
+{
+public:
+ WASABI_SERVICE_NAME("MP4 Raw Reader");
+ static GUID GetServiceGUID() { return mp4_raw_reader_guid; }
+ ns_error_t WASABICALL FileRawReaderService_CreateRawMediaReader(ifc_raw_media_reader **reader, nx_uri_t filename, nx_file_t file, ifc_metadata *parent_metadata);
+};
+
+class MP4RawReader : public ifc_raw_media_reader
+{
+public:
+ MP4RawReader();
+ ~MP4RawReader();
+ int Initialize(MP4FileHandle file);
+ int WASABICALL RawMediaReader_Read(void *buffer, size_t buffer_size, size_t *bytes_read);
+private:
+ uint16_t track_num;
+ uint32_t number_of_tracks;
+ MP4TrackId current_track;
+ MP4FileHandle file;
+ MP4ChunkId chunk_id;
+ MP4ChunkId number_of_chunks;
+ uint32_t chunk_position, chunk_size;
+ uint8_t *chunk_buffer;
+ int ReadNextChunk();
+}; \ No newline at end of file
diff --git a/Src/replicant/mp4/api.h b/Src/replicant/mp4/api.h
new file mode 100644
index 000000000..24e247abb
--- /dev/null
+++ b/Src/replicant/mp4/api.h
@@ -0,0 +1,16 @@
+#pragma once
+#include "service/api_service.h"
+extern api_service *serviceManager;
+#define WASABI2_API_SVC serviceManager
+
+#include "application/api_application.h"
+extern api_application *applicationApi;
+#define WASABI2_API_APP applicationApi
+
+#include "metadata/api_metadata.h"
+extern api_metadata *metadata_api;
+#define REPLICANT_API_METADATA metadata_api
+
+#include "filelock/api_filelock.h"
+extern api_filelock *filelock_api;
+#define REPLICANT_API_FILELOCK filelock_api \ No newline at end of file
diff --git a/Src/replicant/mp4/ifc_mp4audiodecoder.h b/Src/replicant/mp4/ifc_mp4audiodecoder.h
new file mode 100644
index 000000000..7c1ffa656
--- /dev/null
+++ b/Src/replicant/mp4/ifc_mp4audiodecoder.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "ifc_mp4file.h"
+#include "audio/ifc_audioout.h"
+
+class ifc_mp4audiodecoder : public Wasabi2::Dispatchable
+{
+ protected:
+ ifc_mp4audiodecoder() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_mp4audiodecoder() {}
+public:
+ int FillAudioParameters(ifc_audioout::Parameters *parameters) { return MP4AudioDecoder_FillAudioParameters(parameters); }
+ int Decode(const void **output_buffer, size_t *output_buffer_bytes, double *start_position, double *end_position) { return MP4AudioDecoder_Decode(output_buffer, output_buffer_bytes, start_position, end_position); }
+ int Seek(ifc_mp4file::SampleID sample_number) { return MP4AudioDecoder_Seek(sample_number); }
+ int SeekSeconds(double *seconds) { return MP4AudioDecoder_SeekSeconds(seconds); }
+ int ConnectFile(ifc_mp4file *new_file) { return MP4AudioDecoder_ConnectFile(new_file); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+private:
+ /* sizeof_parameters will already be filled out for you */
+ virtual int WASABICALL MP4AudioDecoder_FillAudioParameters(ifc_audioout::Parameters *parameters)=0;
+ virtual int WASABICALL MP4AudioDecoder_Decode(const void **output_buffer, size_t *output_buffer_bytes, double *start_position, double *end_position)=0;
+ virtual int WASABICALL MP4AudioDecoder_Seek(ifc_mp4file::SampleID sample_number) = 0;
+ /* fill in with the actual seconds you'll resume playback at */
+ virtual int WASABICALL MP4AudioDecoder_SeekSeconds(double *seconds) = 0;
+ /* this is an unfortunate wart in the API. In order to support editing metadata on an actively playing file, we have to re-open the file which will generate a new ifc_mp4file object.
+ do _not_ reset the decoder or change the sample number. You should assume the file is identical from a playback point-of-view, just release your old object and retain/assign the new one */
+ virtual int WASABICALL MP4AudioDecoder_ConnectFile(ifc_mp4file *new_file)=0;
+};
diff --git a/Src/replicant/mp4/ifc_mp4file.h b/Src/replicant/mp4/ifc_mp4file.h
new file mode 100644
index 000000000..310811d39
--- /dev/null
+++ b/Src/replicant/mp4/ifc_mp4file.h
@@ -0,0 +1,72 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+static const uint32_t mp4file_invalid_track_id=0;
+
+class ifc_mp4file : public Wasabi2::Dispatchable
+{
+ protected:
+ ifc_mp4file() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_mp4file() {}
+public:
+ typedef uint32_t TrackID;
+ typedef uint32_t SampleID;
+ typedef uint64_t Timestamp;
+ typedef uint64_t Duration;
+ typedef struct metadata_itunes_atom_s {} *metadata_itunes_atom_t;
+
+ int Free(void *buffer) { return MP4File_Free(buffer); }
+
+ int Track_GetESConfiguration(TrackID track_number, uint8_t **buffer, uint32_t *buffer_size) { return MP4File_Track_GetESConfiguration(track_number, buffer, buffer_size); }
+ int Track_GetMaxSampleSize(TrackID track_number, uint32_t *max_sample_size) { return MP4File_Track_GetMaxSampleSize(track_number, max_sample_size); }
+ int Track_ConvertFromTimestamp(TrackID track_number, Timestamp timestamp, double *seconds) { return MP4File_Track_ConvertFromTimestamp(track_number, timestamp, seconds); }
+ int Track_ConvertToDuration(TrackID track_number, double seconds, Duration *duration) { return MP4File_Track_ConvertToDuration(track_number, seconds, duration); }
+ int Track_GetMediaDataName(TrackID track_number, const char **name) { return MP4File_Track_GetMediaDataName(track_number, name); }
+ int Track_GetESDSObjectTypeID(TrackID track_number, uint8_t *type) { return MP4File_Track_GetESDSObjectTypeID(track_number, type); }
+ int Track_GetAudioMPEG4Type(TrackID track_number, uint8_t *type) { return MP4File_Track_GetAudioMPEG4Type(track_number, type); }
+ int Track_GetBytesProperty(TrackID track_number, const char *property_name, uint8_t **buffer, uint32_t *buffer_size) { return MP4File_Track_GetBytesProperty(track_number, property_name, buffer, buffer_size); }
+
+ int Metadata_iTunes_FindFreeform(const char *name, const char *mean, metadata_itunes_atom_t *atom) { return MP4File_Metadata_iTunes_FindFreeform(name, mean, atom); }
+ int Metadata_iTunes_GetBinary(metadata_itunes_atom_t atom, const uint8_t **value, size_t *value_length) { return MP4File_Metadata_iTunes_GetBinary(atom, value, value_length); }
+
+ int Sample_Read(TrackID track_number, SampleID sample_number, uint8_t **bytes, uint32_t *bytes_length, Timestamp *start_time=0, Duration *duration=0, Duration *offset=0, int *is_sync=0) { return MP4File_Sample_Read(track_number, sample_number, bytes, bytes_length, start_time, duration, offset, is_sync); }
+ int Sample_GetFromDuration(TrackID track_number, Duration duration, SampleID *sample_id) { return MP4File_Sample_GetFromDuration(track_number, duration, sample_id); }
+
+
+ enum
+ {
+ esds_object_type_mpeg4_audio = 0x40,
+ esds_object_type_mpeg2_aac_lc_audio = 0x67,
+ esds_object_type_mpeg2_audio = 0x69,
+ esds_object_type_mpeg1_audio = 0x6B,
+
+ mpeg4_audio_type_aac_lc = 2,
+ mpeg4_audio_type_he_aac = 5,
+ mpeg4_audio_type_layer1 = 32,
+ mpeg4_audio_type_layer2 = 33,
+ mpeg4_audio_type_layer3 = 34,
+ };
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+private:
+ virtual int WASABICALL MP4File_Free(void *buffer)=0;
+
+ virtual int WASABICALL MP4File_Track_GetESConfiguration(TrackID track_number, uint8_t **buffer, uint32_t *buffer_size)=0;
+ virtual int WASABICALL MP4File_Track_GetMaxSampleSize(TrackID track_number, uint32_t *max_sample_size)=0;
+ virtual int WASABICALL MP4File_Track_ConvertFromTimestamp(TrackID track_number, Timestamp timestamp, double *seconds)=0;
+ virtual int WASABICALL MP4File_Track_ConvertToDuration(TrackID track_number, double seconds, Duration *duration)=0;
+ virtual int WASABICALL MP4File_Track_GetMediaDataName(TrackID track_number, const char **name)=0;
+ virtual int WASABICALL MP4File_Track_GetESDSObjectTypeID(TrackID track_number, uint8_t *type)=0;
+ virtual int WASABICALL MP4File_Track_GetAudioMPEG4Type(TrackID track_number, uint8_t *type)=0;
+ virtual int WASABICALL MP4File_Track_GetBytesProperty(TrackID track_number, const char *property_name, uint8_t **buffer, uint32_t *buffer_size)=0;
+
+ virtual int WASABICALL MP4File_Metadata_iTunes_FindFreeform(const char *name, const char *mean, metadata_itunes_atom_t *atom)=0;
+ virtual int WASABICALL MP4File_Metadata_iTunes_GetBinary(metadata_itunes_atom_t atom, const uint8_t **value, size_t *value_length)=0;
+
+ virtual int WASABICALL MP4File_Sample_Read(TrackID track_number, SampleID sample_number, uint8_t **bytes, uint32_t *bytes_length, Timestamp *start_time, Duration *duration, Duration *offset, int *is_sync)=0;
+ virtual int WASABICALL MP4File_Sample_GetFromDuration(TrackID track_number, Duration duration, SampleID *sample_id)=0;
+
+};
diff --git a/Src/replicant/mp4/main.h b/Src/replicant/mp4/main.h
new file mode 100644
index 000000000..b997a277d
--- /dev/null
+++ b/Src/replicant/mp4/main.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "nx/nxuri.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ enum
+ {
+ EXTENSION_FOR_PLAYBACK,
+ EXTENSION_FOR_METADATA,
+ EXTENSION_FOR_AUDIO_DECODE,
+ };
+ bool IsMyExtension(nx_uri_t filename, int search_style);
+ int EnumerateExtensions(unsigned int index, nx_string_t *extension, int search_style);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/mp4/precomp.h b/Src/replicant/mp4/precomp.h
new file mode 100644
index 000000000..4a141102b
--- /dev/null
+++ b/Src/replicant/mp4/precomp.h
@@ -0,0 +1,32 @@
+//
+// precomp.h
+// mp4
+//
+
+#include <assert.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 "mp4.h"
+#include "nsmp4.h"
+#include "nu/PtrDeque.h"
+#include "jnetlib/jnetlib.h"
+#include "nswasabi/ReferenceCounted.h"
+#include "nswasabi/ServiceName.h"
+#include "decode/svc_decode.h"
+#include "http/svc_http_demuxer.h"
+#include "replaygain/ifc_replaygain_settings.h"
+#include "service/ifc_servicefactory.h"
+#endif \ No newline at end of file
diff --git a/Src/replicant/mp4/svc_mp4decoder.h b/Src/replicant/mp4/svc_mp4decoder.h
new file mode 100644
index 000000000..d8e242add
--- /dev/null
+++ b/Src/replicant/mp4/svc_mp4decoder.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "ifc_mp4file.h"
+
+class ifc_mp4audiodecoder;
+class ifc_mp4videodecoder;
+
+// {39A53910-CCFE-465D-A46C-F0B95C7DD257}
+static const GUID mp4_decoder_service_type_guid =
+{ 0x39a53910, 0xccfe, 0x465d, { 0xa4, 0x6c, 0xf0, 0xb9, 0x5c, 0x7d, 0xd2, 0x57 } };
+
+class svc_mp4decoder : public Wasabi2::Dispatchable
+{
+protected:
+ svc_mp4decoder() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_mp4decoder() {}
+public:
+ static GUID GetServiceType() { return mp4_decoder_service_type_guid; }
+ int CreateAudioDecoder(ifc_mp4file *mp4_file, ifc_mp4file::TrackID mp4_track, ifc_mp4audiodecoder **decoder) { return MP4DecoderService_CreateAudioDecoder(mp4_file, mp4_track, decoder); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual int WASABICALL MP4DecoderService_CreateAudioDecoder(ifc_mp4file *mp4_file, ifc_mp4file::TrackID mp4_track, ifc_mp4audiodecoder **decoder)=0;
+};
diff --git a/Src/replicant/nsaac/ADTSHeader.c b/Src/replicant/nsaac/ADTSHeader.c
new file mode 100644
index 000000000..6796c462f
--- /dev/null
+++ b/Src/replicant/nsaac/ADTSHeader.c
@@ -0,0 +1,116 @@
+#include "ADTSHeader.h"
+#include "foundation/error.h"
+
+enum
+ {
+ ADTS_NOT_PROTECTED=1,
+ ADTS_PROTECTED=0,
+ ADTS_SYNC = 0xFFF,
+ ADTS_MAIN = 0x00,
+ ADTS_LC = 0x01,
+ ADTS_SSR = 0x10,
+ ADTS_LTP = 0x11,
+ };
+
+
+int nsaac_adts_parse(nsaac_adts_header_t header, const uint8_t *buffer)
+{
+ unsigned int syncword = (buffer[0] << 4) | (buffer[1] >> 4);
+ unsigned int layer;
+ unsigned int sample_rate_index;
+ if (syncword != ADTS_SYNC)
+ return NErr_LostSynchronization;
+
+ header->syncword = syncword;
+ header->id = (buffer[1] >> 3) & 1;
+ layer = (buffer[1] >> 1) & 3;
+ if (layer != 0)
+ return NErr_WrongFormat;
+ header->layer = layer;
+ header->protection = (buffer[1]) & 1;
+ header->profile = (buffer[2] >> 6) & 3;
+ sample_rate_index = (buffer[2] >> 2) & 0xF;
+ if (sample_rate_index == 15)
+ return NErr_UnsupportedFormat; // might actually be OK if we can separately signal the sample rate
+
+ if (sample_rate_index > 13)
+ return NErr_Reserved;
+
+ header->sample_rate_index = sample_rate_index;
+ header->private_bit = (buffer[2] >> 1) & 1;
+ header->channel_configuration = ((buffer[2] & 1) << 2) | ((buffer[3] >> 6) & 3);
+ header->original = (buffer[3] >> 5) &1;
+ header->home = (buffer[3] >> 4) &1;
+
+ //copyright_identification_bit = (buffer[3] >> 3) & 1;
+ //copyright_identification_start = (buffer[3] >> 2) & 1;
+ header->frame_length = ((buffer[3] & 3) << 11) | (buffer[4]<<3) | ((buffer[5] >> 5) & 7);
+ header->buffer_fullness = ((buffer[5] & 0x1F) << 6) | (buffer[6] >> 2);
+ header->num_data_blocks = buffer[6] & 3;
+ return NErr_Success;
+}
+
+
+static const unsigned int aac_sratetab[] =
+ {
+ 96000,
+ 88200,
+ 64000,
+ 48000,
+ 44100,
+ 32000,
+ 24000,
+ 22050,
+ 16000,
+ 12000,
+ 11025,
+ 8000,
+ 7350,
+ };
+
+unsigned int nsaac_adts_get_samplerate(nsaac_adts_header_t header)
+{
+ return aac_sratetab[header->sample_rate_index];
+}
+
+int nsaac_adts_match(nsaac_adts_header_t header1, nsaac_adts_header_t header2)
+{
+ if (header1->id != header2->id)
+ return NErr_False;
+
+ if (header1->profile != header2->profile)
+ return NErr_False;
+
+ if (header1->sample_rate_index != header2->sample_rate_index)
+ return NErr_False;
+
+ if (header1->channel_configuration != header2->channel_configuration)
+ return NErr_False;
+
+ return NErr_True;
+}
+
+int nsaac_adts_get_channel_count(nsaac_adts_header_t header)
+{
+ switch(header->channel_configuration)
+ {
+ case 7:
+ return 8;
+ default:
+ return header->channel_configuration;
+ }
+}
+
+size_t nsaac_adts_get_header_size(nsaac_adts_header_t header)
+{
+ if (header->protection == ADTS_PROTECTED)
+ return 9;
+ else
+ return 7;
+}
+
+size_t nsaac_adts_get_frame_bitrate(nsaac_adts_header_t header)
+{
+ uint64_t bitrate = nsaac_adts_get_samplerate(header) * header->frame_length * 8 / 1024;
+ return (size_t)bitrate;
+} \ No newline at end of file
diff --git a/Src/replicant/nsaac/ADTSHeader.h b/Src/replicant/nsaac/ADTSHeader.h
new file mode 100644
index 000000000..9949b0854
--- /dev/null
+++ b/Src/replicant/nsaac/ADTSHeader.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "foundation/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ADTSHeader
+{
+ unsigned int syncword;
+ unsigned int layer;
+ unsigned int id;
+ unsigned int protection;
+ unsigned int profile;
+ unsigned int sample_rate_index;
+ unsigned int private_bit;
+ unsigned int channel_configuration;
+ unsigned int original;
+ unsigned int home;
+ size_t frame_length;
+ unsigned int buffer_fullness;
+ unsigned int num_data_blocks;
+} ADTSHeader, *nsaac_adts_header_t;
+
+/* must be 7 bytes */
+int nsaac_adts_parse(nsaac_adts_header_t header, const uint8_t *buffer);
+unsigned int nsaac_adts_get_samplerate(nsaac_adts_header_t header);
+int nsaac_adts_match(nsaac_adts_header_t header1, nsaac_adts_header_t header2);
+int nsaac_adts_get_channel_count(nsaac_adts_header_t header);
+size_t nsaac_adts_get_header_size(nsaac_adts_header_t header);
+size_t nsaac_adts_get_frame_bitrate(nsaac_adts_header_t header);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nsapev2/VERSION b/Src/replicant/nsapev2/VERSION
new file mode 100644
index 000000000..ea710abb9
--- /dev/null
+++ b/Src/replicant/nsapev2/VERSION
@@ -0,0 +1 @@
+1.2 \ No newline at end of file
diff --git a/Src/replicant/nsapev2/flags.h b/Src/replicant/nsapev2/flags.h
new file mode 100644
index 000000000..621857f1c
--- /dev/null
+++ b/Src/replicant/nsapev2/flags.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "foundation/error.h"
+namespace APEv2
+{
+ /*
+ http://wiki.hydrogenaudio.org/index.php?title=Ape_Tags_Flags
+ */
+ enum
+ {
+ /* flags for header or item */
+ FLAG_READONLY = 1,
+
+ /* header/footer specific flags */
+ FLAG_HEADER_HAS_HEADER = (1 << 31),
+ FLAG_HEADER_NO_FOOTER = (1 << 30),
+ FLAG_HEADER_IS_HEADER = (1 << 29),
+ FLAG_HEADER_ENCODE_MASK = FLAG_READONLY|FLAG_HEADER_HAS_HEADER|FLAG_HEADER_NO_FOOTER,
+ };
+
+}
diff --git a/Src/replicant/nsapev2/header.cpp b/Src/replicant/nsapev2/header.cpp
new file mode 100644
index 000000000..eee96b9a1
--- /dev/null
+++ b/Src/replicant/nsapev2/header.cpp
@@ -0,0 +1,91 @@
+#include "header.h"
+#include "flags.h"
+#include "util.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char apev2_preamble[] = { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' };
+APEv2::Header::Header()
+{
+ memcpy(preamble, apev2_preamble, 8);
+ version=2000;
+ size=0;
+ items=0;
+ flags=0;
+ reserved=0;
+}
+
+APEv2::Header::Header(const void *data)
+{
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, Header::SIZE);
+
+ bytereader_read_n(&byte_reader, preamble, 8);
+ version=bytereader_read_u32_le(&byte_reader);
+ size=bytereader_read_u32_le(&byte_reader);
+ items=bytereader_read_u32_le(&byte_reader);
+ flags=bytereader_read_u32_le(&byte_reader);
+ bytereader_read_n(&byte_reader, &reserved, 8);
+}
+
+uint32_t APEv2::Header::GetFlags() const
+{
+ return flags;
+}
+
+bool APEv2::Header::Valid() const
+{
+ return !memcmp(preamble, apev2_preamble, 8) && reserved == 0;
+}
+
+uint32_t APEv2::Header::TagSize() const
+{
+ size_t size = this->size;
+ if (IsHeader() && HasFooter())
+ size+=SIZE;
+ if (IsFooter() && HasHeader())
+ size+=SIZE;
+
+ if (size > ULONG_MAX)
+ return 0;
+
+ return (uint32_t)size;
+}
+
+bool APEv2::Header::HasHeader() const
+{
+ return !!(flags & FLAG_HEADER_HAS_HEADER);
+}
+
+bool APEv2::Header::HasFooter() const
+{
+ return !(flags & FLAG_HEADER_NO_FOOTER);
+}
+
+bool APEv2::Header::IsFooter() const
+{
+ return !(flags & FLAG_HEADER_IS_HEADER);
+}
+
+bool APEv2::Header::IsHeader() const
+{
+ return !!(flags & FLAG_HEADER_IS_HEADER);
+}
+
+int APEv2::Header::Encode(bytewriter_t byte_writer) const
+{
+ if (bytewriter_size(byte_writer) < 32)
+ return NErr_Insufficient;
+
+ bytewriter_write_n(byte_writer, apev2_preamble, 8);
+ bytewriter_write_u32_le(byte_writer, version);
+ bytewriter_write_u32_le(byte_writer, size);
+ bytewriter_write_u32_le(byte_writer, items);
+ bytewriter_write_u32_le(byte_writer, flags);
+ bytewriter_write_zero_n(byte_writer, 8);
+
+ return NErr_Success;
+} \ No newline at end of file
diff --git a/Src/replicant/nsapev2/header.h b/Src/replicant/nsapev2/header.h
new file mode 100644
index 000000000..462722e1c
--- /dev/null
+++ b/Src/replicant/nsapev2/header.h
@@ -0,0 +1,39 @@
+#pragma once
+#include "foundation/types.h"
+#include "nu/ByteWriter.h"
+class nstest;
+
+namespace APEv2
+{
+ class Header
+ {
+ friend class ::nstest;
+ public:
+ Header();
+ Header(const void *data);
+ void SetSize(uint32_t size) { this->size = size; }
+ void SetFlags(uint32_t flags) { this->flags = flags; }
+ void SetItems(uint32_t items) { this->items = items; }
+ bool Valid() const;
+ uint32_t TagSize() const;
+ bool HasHeader() const;
+ bool HasFooter() const;
+ bool IsFooter() const;
+ bool IsHeader() const;
+ int Encode(bytewriter_t byte_writer) const;
+ uint32_t GetFlags() const;
+ enum
+ {
+ SIZE=32,
+ };
+ private:
+ uint8_t preamble[8];
+ uint32_t version;
+ uint32_t size;
+ uint32_t items;
+ uint32_t flags;
+ uint64_t reserved;
+ };
+}
+
+
diff --git a/Src/replicant/nsapev2/item.cpp b/Src/replicant/nsapev2/item.cpp
new file mode 100644
index 000000000..2d5184ea0
--- /dev/null
+++ b/Src/replicant/nsapev2/item.cpp
@@ -0,0 +1,235 @@
+#include "item.h"
+#include "flags.h"
+#include "util.h"
+#include "nu/ByteWriter.h"
+#include "nu/strsafe.h"
+#include "nu/ByteReader.h"
+#include "nsapev2/nsapev2.h"
+#include <stdlib.h>
+
+/*
+http://wiki.hydrogenaudio.org/index.php?title=APE_Tag_Item
+
+Item layout:
+[0-3] length of value field (little endian)
+[4-7] flags (little endian)
+[null terminated] key
+[length] value
+*/
+
+APEv2::Item::Item()
+{
+ len=0;
+ flags=0;
+ key=0;
+ value=0;
+}
+
+APEv2::Item::~Item()
+{
+ free(key);
+ free(value);
+}
+
+int APEv2::Item::Read(bytereader_t byte_reader)
+{
+ if (bytereader_size(byte_reader) < 8)
+ return NErr_NeedMoreData;
+
+ /* read fixed-size fields */
+ len = bytereader_read_u32_le(byte_reader);
+ flags = bytereader_read_u32_le(byte_reader);
+
+ /* find the null terminator */
+ size_t key_len = bytereader_find_zero(byte_reader);
+
+ /* make sure we didn't hit the end of our buffer */
+ if (key_len == bytereader_size(byte_reader))
+ return NErr_Insufficient;
+
+ /* check for empty key and also check for integer overflow */
+ if (key_len == 0 || key_len+1 == 0)
+ return NErr_Error;
+
+ key = (char *)malloc(key_len+1);
+ if (key)
+ {
+ bytereader_read_n(byte_reader, key, key_len+1); /* read key and terminator*/
+
+ if (bytereader_size(byte_reader) < len) /* make sure we have room for the value! */
+ {
+ free(key);
+ key=0;
+ return NErr_NeedMoreData;
+ }
+
+ value = (char *)malloc(len);
+ if (value)
+ {
+ bytereader_read_n(byte_reader, value, len); /* read value */
+ return NErr_Success;
+ }
+ else
+ {
+ free(key);
+ key=0;
+ return NErr_OutOfMemory;
+ }
+ }
+ else
+ return NErr_OutOfMemory;
+
+}
+
+bool APEv2::Item::IsReadOnly()
+{
+ return flags & FLAG_READONLY;
+}
+
+bool APEv2::Item::KeyMatch(const char *key_to_compare, int compare)
+{
+ if (!key || !*key)
+ return false;
+
+ switch (compare)
+ {
+ case ITEM_KEY_COMPARE_CASE_INSENSITIVE:
+#ifdef _WIN32
+ return !_stricmp(key_to_compare, key);
+#else
+ return !strcasecmp(key_to_compare, key);
+#endif
+ case ITEM_KEY_COMPARE_CASE_SENSITIVE:
+ return !strcmp(key_to_compare, key);
+ default:
+ return false;
+ }
+}
+
+int APEv2::Item::Get(const void **data, size_t *datalen) const
+{
+ if (!value || !len)
+ return NErr_Empty;
+ *data = value;
+ *datalen = len;
+ return NErr_Success;
+}
+
+int APEv2::Item::Set(nx_string_t string)
+{
+ if (!value)
+ return NErr_BadParameter;
+
+ flags &= ~nsapev2_item_type_mask;
+ flags |= nsapev2_item_type_utf8;
+
+ size_t bytes;
+ int ret = NXStringGetBytesSize(&bytes, string, nx_charset_utf8, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+
+ void *new_value = malloc(bytes);
+ if (!new_value)
+ return NErr_OutOfMemory;
+
+ size_t bytes_copied;
+ ret = NXStringGetBytes(&bytes_copied, string, new_value, bytes, nx_charset_utf8, 0);
+ if (ret != NErr_Success)
+ {
+ free(new_value);
+ return ret;
+ }
+
+ free(value);
+ value=new_value;
+ len=(uint32_t)bytes_copied;
+ return NErr_Success;
+}
+
+int APEv2::Item::Set(const void *data, size_t datalen, int data_type)
+{
+ if (!data || !datalen)
+ return NErr_Error;
+
+ // set data type for this item
+ flags &= ~nsapev2_item_type_mask;
+ flags |= data_type;
+
+ void *new_value = realloc(value, datalen);
+ if (!new_value)
+ return NErr_OutOfMemory;
+
+ value=new_value;
+ len=(uint32_t)datalen;
+ memcpy(value, data, len);
+ return NErr_Success;
+}
+
+ int APEv2::Item::New(size_t datalen, int data_type, void **bytes)
+ {
+ if (!datalen)
+ return NErr_Error;
+
+ // set data type for this item
+ flags &= ~nsapev2_item_type_mask;
+ flags |= data_type;
+
+ void *new_value = realloc(value, datalen);
+ if (!new_value)
+ return NErr_OutOfMemory;
+
+ value=new_value;
+
+ len=(uint32_t)datalen;
+ *bytes = value;
+ return NErr_Success;
+ }
+
+int APEv2::Item::SetKey(const char *tag)
+{
+ if (!tag || !*tag)
+ return NErr_Error;
+
+ char *new_key = strdup(tag);
+ if (!new_key)
+ return NErr_OutOfMemory;
+
+ free(key);
+ key = new_key;
+ return NErr_Success;
+}
+
+int APEv2::Item::GetKey(const char **tag) const
+{
+ if (!key)
+ return NErr_Error;
+ *tag = key;
+ return NErr_Success;
+}
+
+size_t APEv2::Item::EncodeSize() const
+{
+ return 4 /* size */ + 4 /* flags */ + strlen(key) + 1 /* NULL separator */ + len;
+}
+
+uint32_t APEv2::Item::GetFlags() const
+{
+ return flags;
+}
+
+int APEv2::Item::Encode(bytewriter_t byte_writer) const
+{
+ if (!key || !value || !len)
+ return NErr_Error;
+
+ if (bytewriter_size(byte_writer) < EncodeSize())
+ return NErr_Insufficient;
+
+ bytewriter_write_u32_le(byte_writer, len);
+ bytewriter_write_u32_le(byte_writer, flags);
+ bytewriter_write_n(byte_writer, key, strlen(key) + 1);
+ bytewriter_write_n(byte_writer, value, len);
+
+ return NErr_Success;
+}
+
diff --git a/Src/replicant/nsapev2/item.h b/Src/replicant/nsapev2/item.h
new file mode 100644
index 000000000..de07d4725
--- /dev/null
+++ b/Src/replicant/nsapev2/item.h
@@ -0,0 +1,43 @@
+#pragma once
+#include "foundation/types.h"
+#include "nx/nxstring.h"
+#include "nu/PtrDeque.h"
+#include "nu/ByteWriter.h"
+#include "nu/ByteReader.h"
+namespace APEv2
+{
+ enum
+ {
+ ITEM_KEY_COMPARE_CASE_INSENSITIVE = 0,
+ ITEM_KEY_COMPARE_CASE_SENSITIVE = 1,
+ };
+
+ class Item : public nu::PtrDequeNode
+ {
+ public:
+ Item();
+ ~Item();
+
+ /* If successful, puts incremented data pointer in new_data, and new data size remaining in new_len */
+ int Read(bytereader_t byte_reader);
+
+ int Encode(bytewriter_t byte_writer) const;
+ size_t EncodeSize() const;
+
+ bool IsReadOnly();
+ bool KeyMatch(const char *key, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE);
+ int Get(const void **data, size_t *len) const;
+ int GetKey(const char **tag) const;
+ int Set(nx_string_t value);
+ int Set(const void *data, size_t len, int dataType);
+ int SetKey(const char *tag);
+ int New(size_t datalen, int data_type, void **bytes);
+ uint32_t GetFlags() const;
+
+ private:
+ uint32_t flags;
+ char *key;
+ void *value;
+ uint32_t len;
+ };
+}
diff --git a/Src/replicant/nsapev2/nsapev2.h b/Src/replicant/nsapev2/nsapev2.h
new file mode 100644
index 000000000..1b703d1e6
--- /dev/null
+++ b/Src/replicant/nsapev2/nsapev2.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "foundation/types.h"
+#include "nx/nxstring.h"
+
+#ifdef __ANDROID__
+#include "android/nsapev2.h"
+#elif defined(_WIN32)
+#include "windows/nsapev2.h"
+#elif defined(__linux__)
+#include "linux/nsapev2.h"
+#elif defined (__APPLE__)
+#include "osx/nsapev2.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct nsapev2_header_struct_t { } *nsapev2_header_t;
+ typedef struct nsapev2_tag_struct_t { } *nsapev2_tag_t;
+ typedef struct nsapev2_item_struct_t { } *nsapev2_item_t;
+
+ // must be exactly 32 bytes
+ NSAPEV2_EXPORT int NSAPEv2_Header_Valid(const void *header_data);
+ NSAPEV2_EXPORT int NSAPEv2_Header_Create(nsapev2_header_t *header, const void *header_data, size_t header_len);
+ NSAPEV2_EXPORT int NSAPEv2_Header_TagSize(const nsapev2_header_t header, uint32_t *tag_size);
+ NSAPEV2_EXPORT int NSAPEv2_Header_HasHeader(nsapev2_header_t header);
+ NSAPEV2_EXPORT int NSAPEv2_Header_Destroy(nsapev2_header_t header);
+
+
+ // currently, this function makes a copy of any necessary data. in the future, it would be better
+ // to make another version of this function that "borrows" your data
+ // if you can guarantee that the memory will outlive the nsapev2_tag_t object
+ NSAPEV2_EXPORT int NSAPEv2_Tag_Create(nsapev2_tag_t *tag, const nsapev2_header_t header, const void *bytes, size_t bytes_len);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_New(nsapev2_tag_t *tag);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_SerializedSize(const nsapev2_tag_t tag, size_t *bytes);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_Serialize(const nsapev2_tag_t t, void *data, size_t bytes);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_Destroy(nsapev2_tag_t tag);
+
+ NSAPEV2_EXPORT int NSAPEv2_Tag_GetFlags(nsapev2_tag_t tag, uint32_t *flags);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_SetFlags(nsapev2_tag_t tag, uint32_t flags, uint32_t mask);
+
+ NSAPEV2_EXPORT int NSAPEv2_Tag_GetString(const nsapev2_tag_t tag, const char *key, unsigned int index, nx_string_t *value);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_SetString(nsapev2_tag_t tag, const char *key, unsigned int index, nx_string_t value);
+
+ NSAPEV2_EXPORT int NSAPEv2_Tag_GetBinary(const nsapev2_tag_t tag, const char *key, unsigned int index, const void **bytes, size_t *length);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_SetBinary(nsapev2_tag_t tag, const char *key, unsigned int index, const void *bytes, size_t length);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_SetArtwork(nsapev2_tag_t t, const char *key, unsigned int index, const char *filename, const void *bytes, size_t length);
+ /* Items */
+ // flags
+ static const uint32_t nsapev2_item_read_write = 0;
+ static const uint32_t nsapev2_item_read_only = 1;
+
+ static const uint32_t nsapev2_item_type_utf8 = 0;
+ static const uint32_t nsapev2_item_type_binary = 1 << 1;
+ static const uint32_t nsapev2_item_type_locator = 2 << 1;
+
+ // mask flags with these to check the different fields
+ static const uint32_t nsapev2_item_type_mask = (1 << 2) | (1 << 1);
+ static const uint32_t nsapev2_item_read_mask = 1 << 0;
+
+ NSAPEV2_EXPORT int NSAPEv2_Tag_EnumerateItems(const nsapev2_tag_t tag, const nsapev2_item_t start, nsapev2_item_t *next);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_GetItemCount(const nsapev2_tag_t t, size_t *count);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_GetItemAtIndex(const nsapev2_tag_t t, unsigned int index, nsapev2_item_t *item);
+ NSAPEV2_EXPORT int NSAPEv2_Item_GetInformation(const nsapev2_item_t item, const char **key, uint32_t *flags);
+ NSAPEV2_EXPORT int NSAPEv2_Item_GetString(const nsapev2_item_t item, nx_string_t *value);
+ NSAPEV2_EXPORT int NSAPEv2_Item_GetBinary(const nsapev2_item_t item, const void **bytes, size_t *length);
+ NSAPEV2_EXPORT int NSAPEv2_Item_SetKey(nsapev2_item_t item, const char *key);
+ NSAPEV2_EXPORT int NSAPEv2_Item_SetString(nsapev2_item_t item, nx_string_t value);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_RemoveItem(nsapev2_tag_t tag, nsapev2_item_t item);
+ NSAPEV2_EXPORT int NSAPEv2_Tag_AddItem(nsapev2_tag_t tag, nsapev2_item_t *item);
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nsapev2/nsapev2.sln b/Src/replicant/nsapev2/nsapev2.sln
new file mode 100644
index 000000000..10f0a861c
--- /dev/null
+++ b/Src/replicant/nsapev2/nsapev2.sln
@@ -0,0 +1,81 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsapev2", "nsapev2.vcxproj", "{BF92BB16-012A-43F4-BC67-30D6D5187598}"
+ ProjectSection(ProjectDependencies) = postProject
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27} = {57C90706-B25D-4ACA-9B33-95CDB2427C27}
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
+ {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|Win32.ActiveCfg = Debug|Win32
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|Win32.Build.0 = Debug|Win32
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|x64.ActiveCfg = Debug|x64
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Debug|x64.Build.0 = Debug|x64
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Release|Win32.ActiveCfg = Release|Win32
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Release|Win32.Build.0 = Release|Win32
+ {BF92BB16-012A-43F4-BC67-30D6D5187598}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nsapev2/nsapev2.vcxproj b/Src/replicant/nsapev2/nsapev2.vcxproj
new file mode 100644
index 000000000..cf514039d
--- /dev/null
+++ b/Src/replicant/nsapev2/nsapev2.vcxproj
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{BF92BB16-012A-43F4-BC67-30D6D5187598}</ProjectGuid>
+ <RootNamespace>nsapev2</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x86_Debug\</OutDir>
+ <IntDir>x86_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x64_Debug\</OutDir>
+ <IntDir>x64_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x86_Release\</OutDir>
+ <IntDir>x86_Release\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x64_Release\</OutDir>
+ <IntDir>x64_Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="header.cpp" />
+ <ClCompile Include="item.cpp" />
+ <ClCompile Include="nsapev2_common.cpp" />
+ <ClCompile Include="tag.cpp" />
+ <ClCompile Include="windows\nsapev2.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="flags.h" />
+ <ClInclude Include="header.h" />
+ <ClInclude Include="item.h" />
+ <ClInclude Include="nsapev2.h" />
+ <ClInclude Include="precomp.h" />
+ <ClInclude Include="tag.h" />
+ <ClInclude Include="util.h" />
+ <ClInclude Include="windows\nsapev2.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nsapev2/nsapev2_common.cpp b/Src/replicant/nsapev2/nsapev2_common.cpp
new file mode 100644
index 000000000..badcb1006
--- /dev/null
+++ b/Src/replicant/nsapev2/nsapev2_common.cpp
@@ -0,0 +1,465 @@
+#include "nsapev2.h"
+#include "nsapev2/header.h"
+#include "nsapev2/tag.h"
+#include <new>
+
+int NSAPEv2_Header_Valid(const void *header_data)
+
+{
+ if (APEv2::Header(header_data).Valid())
+ return NErr_Success;
+ else
+ return NErr_False;
+}
+
+int NSAPEv2_Header_Create(nsapev2_header_t *header, const void *header_data, size_t header_len)
+{
+ if (header_len < 10 || NSAPEv2_Header_Valid(header_data) != NErr_Success)
+ return NErr_Error;
+
+ nsapev2_header_t new_header = (nsapev2_header_t)new (std::nothrow) APEv2::Header(header_data);
+ if (!new_header)
+ return NErr_OutOfMemory;
+ *header = new_header;
+ return NErr_Success;
+}
+
+int NSAPEv2_Header_TagSize(const nsapev2_header_t h, uint32_t *tag_size)
+{
+ const APEv2::Header *header = (const APEv2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ *tag_size = header->TagSize();
+ return NErr_Success;
+}
+
+int NSAPEv2_Header_HasHeader(nsapev2_header_t h)
+{
+ const APEv2::Header *header = (const APEv2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ if (header->HasHeader())
+ return NErr_Success;
+ else
+ return NErr_False;
+}
+
+int NSAPEv2_Header_Destroy(nsapev2_header_t h)
+{
+ const APEv2::Header *header = (const APEv2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ delete header;
+ return NErr_Success;
+}
+
+int NSAPEv2_Tag_Create(nsapev2_tag_t *t, const nsapev2_header_t h, const void *bytes, size_t bytes_len)
+{
+ const APEv2::Header *header = (const APEv2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ APEv2::Tag *tag = new (std::nothrow) APEv2::Tag;
+ if (!tag)
+ return NErr_OutOfMemory;
+
+ int ret = tag->Parse(header, bytes, bytes_len);
+ if (ret != NErr_Success)
+ {
+ delete tag;
+ return ret;
+ }
+
+ *t = (nsapev2_tag_t)tag;
+ return ret;
+}
+
+int NSAPEv2_Tag_New(nsapev2_tag_t *t)
+{
+ APEv2::Header *new_header = new (std::nothrow) APEv2::Header();
+ if (!new_header)
+ return NErr_OutOfMemory;
+
+ APEv2::Tag *tag = new (std::nothrow) APEv2::Tag;
+ if (!tag)
+ {
+ delete new_header;
+ return NErr_OutOfMemory;
+ }
+
+ *t = (nsapev2_tag_t)tag;
+ return NErr_Success;
+}
+
+int NSAPEv2_Tag_SerializedSize(const nsapev2_tag_t t, size_t *bytes)
+{
+ const APEv2::Tag *tag = (const APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ *bytes = tag->EncodeSize();
+ return NErr_Success;
+}
+
+int NSAPEv2_Tag_Serialize(const nsapev2_tag_t t, void *data, size_t bytes)
+{
+ const APEv2::Tag *tag = (const APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ return tag->Encode(data, bytes);
+}
+
+int NSAPEv2_Tag_Destroy(nsapev2_tag_t t)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_NullPointer;
+ delete tag;
+ return NErr_Success;
+}
+
+int NSAPEv2_Tag_GetFlags(nsapev2_tag_t t, uint32_t *flags)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ return tag->GetFlags(flags);
+}
+
+int NSAPEv2_Tag_SetFlags(nsapev2_tag_t t, uint32_t flags, uint32_t mask)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ return tag->SetFlags(flags, mask);
+}
+
+int NSAPEv2_Tag_GetString(const nsapev2_tag_t t, const char *key, unsigned int index, nx_string_t *value)
+{
+ const APEv2::Tag *tag = (const APEv2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ const void *data;
+ size_t data_len;
+ int ret = tag->GetData(key, index, &data, &data_len);
+ if (ret != NErr_Success)
+ return ret;
+
+ return NXStringCreateWithBytes(value, data, data_len, nx_charset_utf8);
+}
+
+int NSAPEv2_Tag_SetString(nsapev2_tag_t t, const char *key, unsigned int index, nx_string_t value)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ if (value)
+ {
+ APEv2::Item *item;
+ int ret = tag->GetItem(key, index, &item);
+ if (ret == NErr_Success)
+ {
+ return item->Set(value);
+ }
+ else
+ {
+ /* no such item, need to make a new one */
+ item = new (std::nothrow) APEv2::Item;
+ if (!item)
+ return NErr_OutOfMemory;
+
+ ret = item->SetKey(key);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ ret = item->Set(value);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ ret = tag->AddItem(item);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ return NErr_Success;
+ }
+ }
+ else
+ {
+ tag->Remove(key, index);
+ return NErr_Success;
+ }
+}
+
+int NSAPEv2_Tag_GetBinary(const nsapev2_tag_t t, const char *key, unsigned int index, const void **bytes, size_t *length)
+{
+ const APEv2::Tag *tag = (const APEv2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ return tag->GetData(key, index, bytes, length);
+}
+
+int NSAPEv2_Tag_SetBinary(nsapev2_tag_t t, const char *key, unsigned int index, const void *bytes, size_t length)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ if (bytes)
+ {
+ APEv2::Item *item;
+ int ret = tag->GetItem(key, index, &item);
+ if (ret == NErr_Success)
+ {
+ return item->Set(bytes, length, nsapev2_item_type_binary);
+ }
+ else
+ {
+ /* no such item, need to make a new one */
+ item = new (std::nothrow) APEv2::Item;
+ if (!item)
+ return NErr_OutOfMemory;
+
+ ret = item->SetKey(key);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ ret = item->Set(bytes, length, nsapev2_item_type_binary);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ ret = tag->AddItem(item);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ return NErr_Success;
+ }
+ }
+ else
+ {
+ tag->Remove(key, index);
+ return NErr_Success;
+ }
+}
+
+static int SetArtwork(APEv2::Item *item, const char *filename, const void *bytes, size_t length)
+{
+
+ size_t filename_length = filename?strlen(filename):0;
+ size_t total_length = filename_length
+ + 1 /* null separator */
+ + length;
+
+ void *item_data;
+ int ret = item->New(total_length, nsapev2_item_type_binary, &item_data);
+ if (ret != NErr_Success)
+ return ret;
+
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, item_data, total_length);
+ bytewriter_write_n(&byte_writer, filename, filename_length);
+ bytewriter_write_u8(&byte_writer, 0);
+ bytewriter_write_n(&byte_writer, bytes, length);
+
+ return NErr_Success;
+}
+
+int NSAPEv2_Tag_SetArtwork(nsapev2_tag_t t, const char *key, unsigned int index, const char *filename, const void *bytes, size_t length)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ if (bytes)
+ {
+
+ APEv2::Item *item;
+ int ret = tag->GetItem(key, index, &item);
+ if (ret == NErr_Success)
+ {
+ return SetArtwork(item, filename, bytes, length);
+ }
+ else
+ {
+ /* no such item, need to make a new one */
+ item = new (std::nothrow) APEv2::Item;
+ if (!item)
+ return NErr_OutOfMemory;
+
+ ret = item->SetKey(key);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ ret = SetArtwork(item, filename, bytes, length);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ ret = tag->AddItem(item);
+ if (ret != NErr_Success)
+ {
+ delete item;
+ return ret;
+ }
+
+ return NErr_Success;
+ }
+ }
+ else
+ {
+ tag->Remove(key, index);
+ return NErr_Success;
+ }
+}
+
+int NSAPEv2_Tag_EnumerateItems(const nsapev2_tag_t t, const nsapev2_item_t s, nsapev2_item_t *next)
+{
+ const APEv2::Tag *tag = (const APEv2::Tag *)t;
+ if (!tag)
+ return NErr_NullPointer;
+
+ const APEv2::Item *start = (const APEv2::Item *)s;
+ return tag->EnumerateItems(start, (APEv2::Item **)next);
+}
+
+int NSAPEv2_Tag_GetItemCount(const nsapev2_tag_t t, size_t *count)
+{
+ const APEv2::Tag *tag = (const APEv2::Tag *)t;
+ if (!tag)
+ return NErr_NullPointer;
+
+ return tag->GetItemCount(count);
+}
+
+
+int NSAPEv2_Tag_GetItemAtIndex(const nsapev2_tag_t t, unsigned int index, nsapev2_item_t *item)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_NullPointer;
+
+ return tag->GetItemAtIndex(index, (APEv2::Item **)item);
+}
+
+int NSAPEv2_Item_GetInformation(const nsapev2_item_t i, const char **key, uint32_t *flags)
+{
+ const APEv2::Item *item = (const APEv2::Item *)i;
+ if (!item)
+ return NErr_NullPointer;
+
+ if (key)
+ {
+ int ret = item->GetKey(key);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ if (flags)
+ *flags = item->GetFlags();
+ return NErr_Success;
+
+}
+
+int NSAPEv2_Item_GetString(const nsapev2_item_t i, nx_string_t *value)
+{
+ const APEv2::Item *item = (const APEv2::Item *)i;
+ if (!item)
+ return NErr_BadParameter;
+
+ const void *data;
+ size_t data_len;
+ int ret = item->Get(&data, &data_len);
+ if (ret != NErr_Success)
+ return ret;
+
+ return NXStringCreateWithBytes(value, data, data_len, nx_charset_utf8);
+}
+
+int NSAPEv2_Item_GetBinary(const nsapev2_item_t i, const void **bytes, size_t *length)
+{
+ const APEv2::Item *item = (const APEv2::Item *)i;
+ if (!item)
+ return NErr_BadParameter;
+
+ return item->Get(bytes, length);
+}
+
+int NSAPEv2_Item_SetKey(nsapev2_item_t i, const char *key)
+{
+ APEv2::Item *item = (APEv2::Item *)i;
+ if (!item)
+ return NErr_BadParameter;
+
+ return item->SetKey(key);
+}
+
+int NSAPEv2_Item_SetString(nsapev2_item_t i, nx_string_t value)
+{
+ APEv2::Item *item = (APEv2::Item *)i;
+ if (!item)
+ return NErr_BadParameter;
+
+ return item->Set(value);
+}
+
+int NSAPEv2_Tag_RemoveItem(nsapev2_tag_t t, nsapev2_item_t i)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ APEv2::Item *item = (APEv2::Item *)i;
+ if (!item)
+ return NErr_BadParameter;
+
+ tag->RemoveItem(item);
+ return NErr_Success;
+}
+
+int NSAPEv2_Tag_AddItem(nsapev2_tag_t t, nsapev2_item_t *i)
+{
+ APEv2::Tag *tag = (APEv2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ APEv2::Item **item = (APEv2::Item **)i;
+ if (!item)
+ return NErr_NullPointer;
+
+ *item = new (std::nothrow) APEv2::Item;
+ if (!*item)
+ return NErr_OutOfMemory;
+
+ return tag->AddItem(*item);
+} \ No newline at end of file
diff --git a/Src/replicant/nsapev2/precomp.h b/Src/replicant/nsapev2/precomp.h
new file mode 100644
index 000000000..5d7ef44d4
--- /dev/null
+++ b/Src/replicant/nsapev2/precomp.h
@@ -0,0 +1,26 @@
+//
+// precomp.h
+// nsapev2
+//
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "nu/strsafe.h"
+
+#ifdef __cplusplus
+#include "nu/PtrDeque.h"
+#endif
+
+#include "nx/nxstring.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+#include "header.h"
+#include "new"
+#endif
diff --git a/Src/replicant/nsapev2/tag.cpp b/Src/replicant/nsapev2/tag.cpp
new file mode 100644
index 000000000..fcaeb0910
--- /dev/null
+++ b/Src/replicant/nsapev2/tag.cpp
@@ -0,0 +1,285 @@
+#include "tag.h"
+#include "header.h"
+#include "flags.h"
+#ifdef _WIN32
+#include "../nu/ns_wc.h"
+#include "nu/strsafe.h"
+#endif
+#include <limits.h>
+
+#include <new>
+#include "nu/ByteWriter.h"
+
+/*
+http://wiki.hydrogenaudio.org/index.php?title=APEv2_specification
+*/
+
+APEv2::Tag::Tag()
+{
+ flags = 0; // default to writing just a footer
+}
+
+APEv2::Tag::~Tag()
+{
+ items.deleteAll();
+}
+
+/* Parsing */
+int APEv2::Tag::Parse(const APEv2::Header *header, const void *_data, size_t len)
+{
+ flags = header->GetFlags();
+
+ if (header->IsFooter())
+ {
+ flags &= ~FLAG_HEADER_NO_FOOTER; // winamp 5.54 had this flag reversed, so let's correct it
+ if (header->HasHeader())
+ {
+ // TODO: validate header
+ _data = (const uint8_t *)_data + 32;
+ len -= 32;
+ }
+ len -= 32; /* footer is counted in the size */
+ return ParseData(_data, len);
+ }
+ else /* IsHeader() */
+ {
+ if (header->HasFooter())
+ {
+ // TODO: validate footer
+ len -= 32;
+ }
+ return ParseData(_data, len);
+ }
+}
+
+int APEv2::Tag::ParseData(const void *data, size_t len)
+{
+ bytereader_s byte_reader;
+ bytereader_init(&byte_reader, data, len);
+
+ while (bytereader_size(&byte_reader))
+ {
+ Item *item = new (std::nothrow) Item;
+ if (!item)
+ return NErr_OutOfMemory;
+
+ int ret = item->Read(&byte_reader);
+ if (ret == NErr_Success)
+ {
+ items.push_back(item);
+ }
+ else
+ {
+ delete item;
+ return ret;
+ }
+ }
+ return NErr_Success;
+}
+
+/* Retrieving Data */
+int APEv2::Tag::GetItem(const char *key, unsigned int index, Item **item, int compare) const
+{
+ unsigned int i=0;
+ for (ItemList::iterator itr=items.begin();itr!=items.end();itr++)
+ {
+ /* check if it's a string first, and then match the key next (will be faster) */
+ if (itr->KeyMatch(key, compare))
+ {
+ if (i++ < index)
+ continue;
+
+ *item = *itr;
+ return NErr_Success;
+ }
+ }
+
+ if (i > index) /* if we found the key once, but the index was too high */
+ return NErr_EndOfEnumeration;
+ return NErr_Empty;
+}
+
+int APEv2::Tag::GetItemAtIndex(unsigned int index, Item **item) const
+{
+ unsigned int i=0;
+ for (ItemList::iterator itr=items.begin();itr!=items.end();itr++)
+ {
+ if (i++ < index)
+ continue;
+
+ *item = *itr;
+ return NErr_Success;
+ }
+
+ return NErr_EndOfEnumeration;
+}
+
+int APEv2::Tag::GetData(const char *key, unsigned int index, const void **data, size_t *data_len, int compare) const
+{
+ Item *item=0;
+ int ret = GetItem(key, index, &item, compare);
+ if (ret != NErr_Success)
+ return ret;
+
+ return item->Get(data, data_len);
+}
+
+int APEv2::Tag::EnumerateItems(const Item *start, Item **item) const
+{
+ Item *next_item = 0;
+ if (!start)
+ {
+ next_item = items.front();
+ }
+ else
+ {
+ next_item = static_cast<APEv2::Item *>(start->next);
+ }
+ *item = next_item;
+ if (next_item)
+ return NErr_Success;
+ else if (start)
+ return NErr_EndOfEnumeration;
+ else
+ return NErr_Empty;
+}
+
+int APEv2::Tag::FindItemByKey(const char *key, Item **item, int compare) const
+{
+ for (ItemList::iterator itr=items.begin();itr!=items.end();itr++)
+ {
+ if (itr->KeyMatch(key, compare))
+ {
+ *item = *itr;
+ return NErr_Success;
+ }
+ }
+ return NErr_Unknown;
+}
+
+bool APEv2::Tag::IsReadOnly() const
+{
+ return flags & FLAG_READONLY;
+}
+
+int APEv2::Tag::GetItemCount(size_t *count) const
+{
+ *count = items.size();
+ return NErr_Success;
+}
+
+int APEv2::Tag::GetFlags(uint32_t *flags) const
+{
+ *flags = this->flags;
+ return NErr_Success;
+}
+
+/* Setting Data */
+int APEv2::Tag::AddItem(APEv2::Item *new_item)
+{
+ items.push_back(new_item);
+ return NErr_Success;
+}
+
+int APEv2::Tag::SetFlags(uint32_t newflags, uint32_t mask)
+{
+ flags = (flags & ~mask) | newflags;
+ return NErr_Success;
+}
+
+/* Removing Data */
+void APEv2::Tag::Clear()
+{
+ items.deleteAll();
+}
+
+void APEv2::Tag::Remove(const char *key, unsigned int starting_index, int compare)
+{
+ for (ItemList::iterator itr=items.begin();itr!=items.end();)
+ {
+ ItemList::iterator next = itr;
+ next++;
+ APEv2::Item *item = *itr;
+
+ if (item->KeyMatch(key, compare))
+ {
+ if (starting_index)
+ {
+ starting_index--;
+ }
+ else
+ {
+ items.erase(item);
+ delete item;
+ }
+ }
+ itr=next;
+ }
+}
+
+void APEv2::Tag::RemoveItem(Item *item)
+{
+ items.erase(item);
+ delete item;
+}
+
+/* Serializing */
+size_t APEv2::Tag::EncodeSize() const
+{
+ size_t total_size=0;
+
+ if (flags & FLAG_HEADER_HAS_HEADER)
+ total_size+=Header::SIZE;
+
+ for (ItemList::iterator itr=items.begin();itr!=items.end();itr++)
+ {
+ total_size += itr->EncodeSize();
+ }
+
+ if (!(flags & FLAG_HEADER_NO_FOOTER))
+ total_size+=Header::SIZE;
+
+ return total_size;
+}
+
+int APEv2::Tag::Encode(void *data, size_t len) const
+{
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, len);
+
+ if (flags & FLAG_HEADER_HAS_HEADER)
+ {
+ Header header;
+ header.SetSize((uint32_t)len - Header::SIZE);
+ header.SetItems((uint32_t)items.size());
+ header.SetFlags((flags & FLAG_HEADER_ENCODE_MASK)|FLAG_HEADER_IS_HEADER);
+
+ int ret = header.Encode(&byte_writer);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ for (ItemList::iterator itr=items.begin();itr!=items.end();itr++)
+ {
+ int ret = itr->Encode(&byte_writer);
+ if (ret!= NErr_Success)
+ return ret;
+ }
+
+ if (!(flags & FLAG_HEADER_NO_FOOTER))
+ {
+ Header footer;
+
+ if (flags & FLAG_HEADER_HAS_HEADER)
+ footer.SetSize((uint32_t)len - Header::SIZE);
+ else
+ footer.SetSize((uint32_t)len);
+ footer.SetItems((uint32_t)items.size());
+ footer.SetFlags((flags & FLAG_HEADER_ENCODE_MASK));
+
+ int ret = footer.Encode(&byte_writer);
+ if (ret != NErr_Success)
+ return ret;
+ }
+ return NErr_Success;
+}
diff --git a/Src/replicant/nsapev2/tag.h b/Src/replicant/nsapev2/tag.h
new file mode 100644
index 000000000..a43c04993
--- /dev/null
+++ b/Src/replicant/nsapev2/tag.h
@@ -0,0 +1,50 @@
+#pragma once
+#include "foundation/types.h"
+#include "nu/PtrDeque.h"
+#include "item.h"
+#include "flags.h"
+#include "header.h"
+
+namespace APEv2
+{
+class Tag
+{
+public:
+ Tag();
+ ~Tag();
+
+ /* Parsing */
+ int Parse(const APEv2::Header *header, const void *data, size_t len);
+
+ /* Retrieving Data */
+ int GetItem(const char *key, unsigned int index, Item **item, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE) const;
+ int GetItemAtIndex(unsigned int index, Item **) const;
+ int GetData(const char *tag, unsigned int index, const void **data, size_t *data_len, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE) const;
+ int EnumerateItems(const Item *start, Item **item) const;
+ int FindItemByKey(const char *key, Item **item, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE) const;
+ bool IsReadOnly() const;
+ int GetItemCount(size_t *count) const;
+ int GetFlags(uint32_t *flags) const;
+
+ /* Setting Data */
+ int AddItem(APEv2::Item *new_item);
+ int SetFlags(uint32_t flags, uint32_t mask);
+
+ /* Removing Data */
+ void Clear();
+ void Remove(const char *key, unsigned int starting_index, int compare=ITEM_KEY_COMPARE_CASE_INSENSITIVE); // removes all items matching a key, but skips the first 'starting_index' items
+ void RemoveItem(Item *item);
+ /* Serializing */
+ size_t EncodeSize() const;
+ int Encode(void *data, size_t len) const;
+
+private: /* methods */
+ int ParseData(const void *data, size_t len); /* helper function, call with data pointing to beginning of items block (skip header), and length without footer. */
+
+private: /* data */
+ typedef nu::PtrDeque<Item> ItemList;
+ ItemList items;
+ uint32_t flags;
+};
+}
+
diff --git a/Src/replicant/nsapev2/util.h b/Src/replicant/nsapev2/util.h
new file mode 100644
index 000000000..6f70f09be
--- /dev/null
+++ b/Src/replicant/nsapev2/util.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/Src/replicant/nsapev2/windows/nsapev2.cpp b/Src/replicant/nsapev2/windows/nsapev2.cpp
new file mode 100644
index 000000000..b8d4ee0c4
--- /dev/null
+++ b/Src/replicant/nsapev2/windows/nsapev2.cpp
@@ -0,0 +1 @@
+#include "nsapev2/nsapev2.h"
diff --git a/Src/replicant/nsapev2/windows/nsapev2.h b/Src/replicant/nsapev2/windows/nsapev2.h
new file mode 100644
index 000000000..cdf0c17c8
--- /dev/null
+++ b/Src/replicant/nsapev2/windows/nsapev2.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "foundation/export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+#ifdef NSID3V2_EXPORTS
+#define NSAPEV2_EXPORT __declspec(dllexport)
+#else
+#define NSAPEV2_EXPORT __declspec(dllimport)
+#endif
+*/
+#define NSAPEV2_EXPORT
+
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nsid3v1/VERSION b/Src/replicant/nsid3v1/VERSION
new file mode 100644
index 000000000..ea710abb9
--- /dev/null
+++ b/Src/replicant/nsid3v1/VERSION
@@ -0,0 +1 @@
+1.2 \ No newline at end of file
diff --git a/Src/replicant/nsid3v1/nsid3v1.cpp b/Src/replicant/nsid3v1/nsid3v1.cpp
new file mode 100644
index 000000000..b96c095fd
--- /dev/null
+++ b/Src/replicant/nsid3v1/nsid3v1.cpp
@@ -0,0 +1,350 @@
+#include "nsid3v1.h"
+#include "tag.h"
+
+#include "foundation/error.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <new>
+
+
+int NSID3v1_Header_Valid(const void *data, size_t len)
+{
+ // return NErr_True or NErr_False depending on whether or not you detect that it's valid ID3v1
+ if (memcmp(data, "TAG", 3) == 0)
+ {
+ return NErr_True;
+ }
+
+ return NErr_False;
+}
+
+int NSID3v1_Tag_Create(const void *data, size_t len, nsid3v1_tag_t *out_tag)
+{
+ ID3v1::Tag *tag = new (std::nothrow) ID3v1::Tag();
+ if (!tag)
+ return NErr_OutOfMemory;
+
+ int ret = tag->Parse(data, len);
+ if (ret != NErr_Success)
+ return ret;
+
+ *out_tag = (nsid3v1_tag_t)tag;
+
+ return NErr_Success;
+}
+
+int NSID3v1_Tag_New(nsid3v1_tag_t *out_tag)
+{
+ ID3v1::Tag *tag = new (std::nothrow) ID3v1::Tag();
+ if (!tag)
+ return NErr_OutOfMemory;
+
+ tag->New();
+
+ *out_tag = (nsid3v1_tag_t)tag;
+
+ return NErr_Success;
+}
+
+int NSID3v1_Tag_Serialize(const nsid3v1_tag_t t, void *data, size_t len)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ return tag->Serialize(data, len);
+}
+
+int NSID3v1_Tag_Destroy(nsid3v1_tag_t t)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+ delete tag;
+ return NErr_Success;
+}
+
+
+// Field getters
+int NSID3v1_Field_Text_Get(const nsid3v1_tag_t t, const int field, nx_string_t *out_value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ char track_num[4] = { 0, 0, 0 };
+
+ if (tag)
+ {
+ switch (field)
+ {
+ case NSID3V1_TITLE:
+ return NSID3v1_Get_Title(t, out_value);
+ case NSID3V1_ARTIST:
+ return NSID3v1_Get_Artist(t, out_value);
+ case NSID3V1_ALBUM:
+ return NSID3v1_Get_Album(t, out_value);
+ case NSID3V1_YEAR:
+ return NSID3v1_Get_Year(t, out_value);
+ case NSID3V1_COMMENT:
+ return NSID3v1_Get_Comment(t, out_value);
+ case NSID3V1_TRACK:
+ return NSID3v1_Get_Track(t, out_value);
+ //case NSID3V1_GENRE:
+ //return NSID3v1_Get_Genre(t, out_value);
+ default:
+ return NErr_Unknown;
+ }
+ }
+ else
+ return NErr_Empty;
+}
+
+int NSID3v1_Get_Title(nsid3v1_tag_t t, nx_string_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ size_t value_length = tag->GetTitleLength();
+ if (value_length > 0)
+ return NXStringCreateWithBytes(value, tag->GetTitle(), value_length, nx_charset_latin1);
+ else
+ return NErr_Empty;
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Get_Artist(nsid3v1_tag_t t, nx_string_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ size_t value_length = tag->GetArtistLength();
+ if (value_length > 0)
+ return NXStringCreateWithBytes(value, tag->GetArtist(), value_length, nx_charset_latin1);
+ else
+ return NErr_Empty;
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Get_Album(nsid3v1_tag_t t, nx_string_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ size_t value_length = tag->GetAlbumLength();
+ if (value_length > 0)
+ return NXStringCreateWithBytes(value, tag->GetAlbum(), value_length, nx_charset_latin1);
+ else
+ return NErr_Empty;
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Get_Year(nsid3v1_tag_t t, nx_string_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ size_t value_length = tag->GetYearLength();
+ if (value_length > 0)
+ return NXStringCreateWithBytes(value, tag->GetYear(), value_length, nx_charset_latin1);
+ else
+ return NErr_Empty;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Get_Comment(nsid3v1_tag_t t, nx_string_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ size_t value_length = tag->GetCommentLength();
+ if (value_length > 0)
+ return NXStringCreateWithBytes(value, tag->GetComment(), value_length, nx_charset_latin1);
+ else
+ return NErr_Empty;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Get_Track(nsid3v1_tag_t t, nx_string_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+
+
+ if (tag)
+ {
+ unsigned char track = tag->GetTrack();
+ if (track > 0)
+ return NXStringCreateWithUInt64(value, track);
+ else
+ return NErr_Empty;
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Int_Get_Year(nsid3v1_tag_t t, unsigned int *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ char year[5];
+ memcpy(year, tag->GetYear(), 4);
+ year[4]=0;
+ *value = strtoul(year, 0, 10);
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Int_Get_Track(nsid3v1_tag_t t, uint8_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ *value = tag->GetTrack();
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v1_Int_Get_Genre(nsid3v1_tag_t t, uint8_t *value)
+{
+ const ID3v1::Tag *tag = (const ID3v1::Tag *)t;
+ if (tag)
+ {
+ *value = tag->GetGenre();
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+/* ================= setters ================= */
+typedef void (ID3v1::Tag::*Setter)(const char *, size_t length);
+template <size_t limit>
+static int SetFromString(ID3v1::Tag *tag, Setter setter, nx_string_t value)
+{
+ if (!value)
+ {
+ (tag->*setter)(0, 0);
+ return NErr_Success;
+ }
+
+ char temp[limit];
+ size_t bytes_copied;
+ int ret = NXStringGetBytes(&bytes_copied, value, temp, limit, nx_charset_latin1, 0);
+ if (ret != NErr_Success)
+ return ret;
+
+ (tag->*setter)(temp, bytes_copied);
+ return NErr_Success;
+}
+
+int NSID3v1_Set_Title(nsid3v1_tag_t t, nx_string_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+ return SetFromString<30>(tag, &ID3v1::Tag::SetTitle, value);
+}
+
+int NSID3v1_Set_Artist(nsid3v1_tag_t t, nx_string_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+ return SetFromString<30>(tag, &ID3v1::Tag::SetArtist, value);
+}
+
+int NSID3v1_Set_Album(nsid3v1_tag_t t, nx_string_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ return SetFromString<30>(tag, &ID3v1::Tag::SetAlbum, value);
+}
+
+int NSID3v1_Set_Year(nsid3v1_tag_t t, nx_string_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ return SetFromString<4>(tag, &ID3v1::Tag::SetYear, value);
+}
+
+int NSID3v1_Set_Comment(nsid3v1_tag_t t, nx_string_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ return SetFromString<28>(tag, &ID3v1::Tag::SetComment, value);
+}
+
+int NSID3v1_Set_Track(nsid3v1_tag_t t, nx_string_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ if (!value)
+ {
+ tag->SetTrack(0);
+ return NErr_Success;
+ }
+
+ int temp=0;
+ int ret = NXStringGetIntegerValue(value, &temp);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (temp < 0 || temp > 255)
+ return NErr_ParameterOutOfRange;
+
+ tag->SetTrack((uint8_t)temp);
+ return NErr_Success;
+}
+
+int NSID3v1_Int_Set_Year(nsid3v1_tag_t t, unsigned int value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ if (value > 9999)
+ return NErr_ParameterOutOfRange;
+
+ char temp[5];
+ sprintf(temp, "%u", value);
+ tag->SetYear(temp, 4);
+
+ return NErr_Success;
+}
+
+int NSID3v1_Int_Set_Track(nsid3v1_tag_t t, uint8_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ tag->SetTrack(value);
+ return NErr_Success;
+}
+
+
+int NSID3v1_Int_Set_Genre(nsid3v1_tag_t t, uint8_t value)
+{
+ ID3v1::Tag *tag = (ID3v1::Tag *)t;
+ if (!t)
+ return NErr_BadParameter;
+
+ tag->SetGenre(value);
+ return NErr_Success;
+}
diff --git a/Src/replicant/nsid3v1/nsid3v1.h b/Src/replicant/nsid3v1/nsid3v1.h
new file mode 100644
index 000000000..dcd6e1559
--- /dev/null
+++ b/Src/replicant/nsid3v1/nsid3v1.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "nx/nxstring.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NSID3V1_EXPORT
+ typedef struct nsid3v1_tag_struct_t { } *nsid3v1_tag_t;
+
+
+
+ // Basic methods
+ NSID3V1_EXPORT int NSID3v1_Header_Valid(const void *data, size_t len);
+ NSID3V1_EXPORT int NSID3v1_Tag_Create(const void *data, size_t len, nsid3v1_tag_t *out_tag);
+ NSID3V1_EXPORT int NSID3v1_Tag_New(nsid3v1_tag_t *out_tag);
+ // len must be >= 128. ALWAYS writes 128 bytes if successful
+ NSID3V1_EXPORT int NSID3v1_Tag_Serialize(const nsid3v1_tag_t t, void *data, size_t len);
+ NSID3V1_EXPORT int NSID3v1_Tag_Destroy(nsid3v1_tag_t t);
+
+ // Generic getters
+ //NSID3V1_EXPORT int NSID3v1_Field_Text_Get(const nsid3v1_tag_t *t, const int field, nx_string_t *out_value);
+ NSID3V1_EXPORT int NSID3v1_Field_Text_Get(const nsid3v1_tag_t t, const int field, nx_string_t *out_value);
+
+ // Specific field getters as text
+ NSID3V1_EXPORT int NSID3v1_Get_Title(const nsid3v1_tag_t t, nx_string_t *value);
+ NSID3V1_EXPORT int NSID3v1_Get_Artist(const nsid3v1_tag_t t, nx_string_t *value);
+ NSID3V1_EXPORT int NSID3v1_Get_Album(const nsid3v1_tag_t t, nx_string_t *value);
+ NSID3V1_EXPORT int NSID3v1_Get_Year(const nsid3v1_tag_t t, nx_string_t *value);
+ NSID3V1_EXPORT int NSID3v1_Get_Comment(const nsid3v1_tag_t t, nx_string_t *value);
+ NSID3V1_EXPORT int NSID3v1_Get_Track(const nsid3v1_tag_t t, nx_string_t *value);
+
+ // Specific field getters as integers
+ NSID3V1_EXPORT int NSID3v1_Int_Get_Year(const nsid3v1_tag_t t, unsigned int *value);
+ NSID3V1_EXPORT int NSID3v1_Int_Get_Track(const nsid3v1_tag_t t, uint8_t *value);
+ NSID3V1_EXPORT int NSID3v1_Int_Get_Genre(const nsid3v1_tag_t t, uint8_t *value);
+
+ // Specific field setters as text
+ NSID3V1_EXPORT int NSID3v1_Set_Title(nsid3v1_tag_t t, nx_string_t value);
+ NSID3V1_EXPORT int NSID3v1_Set_Artist(nsid3v1_tag_t t, nx_string_t value);
+ NSID3V1_EXPORT int NSID3v1_Set_Album(nsid3v1_tag_t t, nx_string_t value);
+ NSID3V1_EXPORT int NSID3v1_Set_Year(nsid3v1_tag_t t, nx_string_t value);
+ NSID3V1_EXPORT int NSID3v1_Set_Comment(nsid3v1_tag_t t, nx_string_t value);
+ NSID3V1_EXPORT int NSID3v1_Set_Track(nsid3v1_tag_t t, nx_string_t value);
+
+ // Specific field setters as integers
+ NSID3V1_EXPORT int NSID3v1_Int_Set_Year(nsid3v1_tag_t t, unsigned int value);
+ NSID3V1_EXPORT int NSID3v1_Int_Set_Track(nsid3v1_tag_t t, uint8_t value);
+ NSID3V1_EXPORT int NSID3v1_Int_Set_Genre(nsid3v1_tag_t t, uint8_t value);
+
+ // field types for ID3V1.0
+ enum
+ {
+ NSID3V1_TAG,
+ NSID3V1_TITLE,
+ NSID3V1_ARTIST,
+ NSID3V1_ALBUM,
+ NSID3V1_YEAR,
+ NSID3V1_COMMENT,
+ NSID3V1_TRACK, // ID3V1.1
+ NSID3V1_GENRE,
+ };
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nsid3v1/nsid3v1.sln b/Src/replicant/nsid3v1/nsid3v1.sln
new file mode 100644
index 000000000..8bf831f5d
--- /dev/null
+++ b/Src/replicant/nsid3v1/nsid3v1.sln
@@ -0,0 +1,44 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsid3v1", "nsid3v1.vcxproj", "{1BF331E2-00BE-4ACC-B491-8C5DA35FA716}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Debug|Win32.ActiveCfg = Debug|Win32
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Debug|Win32.Build.0 = Debug|Win32
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Debug|x64.ActiveCfg = Debug|x64
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Debug|x64.Build.0 = Debug|x64
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Release|Win32.ActiveCfg = Release|Win32
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Release|Win32.Build.0 = Release|Win32
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Release|x64.ActiveCfg = Release|x64
+ {1BF331E2-00BE-4ACC-B491-8C5DA35FA716}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B1F650AF-5553-4512-9413-CCE4ACF1FB2F}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nsid3v1/nsid3v1.vcxproj b/Src/replicant/nsid3v1/nsid3v1.vcxproj
new file mode 100644
index 000000000..9e8b67214
--- /dev/null
+++ b/Src/replicant/nsid3v1/nsid3v1.vcxproj
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{1BF331E2-00BE-4ACC-B491-8C5DA35FA716}</ProjectGuid>
+ <RootNamespace>nsid3v1</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x86_Debug\</OutDir>
+ <IntDir>x86_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x64_Debug\</OutDir>
+ <IntDir>x64_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x86_Release\</OutDir>
+ <IntDir>x86_Release\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x64_Release\</OutDir>
+ <IntDir>x64_Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\nu\nu.vcxproj">
+ <Project>{efc75a79-269f-44fc-bac5-d7d4fd4ec92c}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="nsid3v1.cpp" />
+ <ClCompile Include="tag.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="nsid3v1.h" />
+ <ClInclude Include="precomp.h" />
+ <ClInclude Include="tag.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nsid3v1/precomp.h b/Src/replicant/nsid3v1/precomp.h
new file mode 100644
index 000000000..ccbf5115d
--- /dev/null
+++ b/Src/replicant/nsid3v1/precomp.h
@@ -0,0 +1,18 @@
+//
+// precomp.h
+// nsid3v1
+//
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+
+#include "nx/nxstring.h"
+
+#include <limits.h>
+#include <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
diff --git a/Src/replicant/nsid3v1/tag.cpp b/Src/replicant/nsid3v1/tag.cpp
new file mode 100644
index 000000000..fd0fc163a
--- /dev/null
+++ b/Src/replicant/nsid3v1/tag.cpp
@@ -0,0 +1,177 @@
+#include "tag.h"
+#include <string.h> // for strnlen
+
+#include "nsid3v1.h"
+
+#include "foundation/error.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+
+#ifdef __APPLE__
+
+inline static size_t id3v1_strnlen(const char *s, size_t n)
+{
+ const char *p=(const char *)memchr(s, 0, n);
+ return p?p-s:n;
+}
+
+#else // __APPLE__
+
+inline static size_t id3v1_strnlen(const char *s, size_t n)
+{
+ return strnlen(s, n);
+}
+
+#endif
+
+
+ID3v1::Tag::Tag()
+{
+}
+
+void ID3v1::Tag::New()
+{
+ header[0]='T';
+ header[1]='A';
+ header[2]='G';
+ memset(title, 0, 30);
+ memset(artist, 0, 30);
+ memset(album, 0, 30);
+ memset(year, 0, 30);
+ memset(comment, 0, 30);
+ track=0;
+ genre=0;
+}
+
+// Deprecated, bytereader class can now handle n-number of bytes
+static void GetAndFillNumberOfBytes(bytereader_value_t byte_reader, int bytes, const void *data, char *destination)
+{
+ for (int i = 0; i < bytes; i++)
+ {
+ destination[i] = bytereader_read_u8(&byte_reader);
+ }
+}
+
+const char *ID3v1::Tag::GetHeader(void) const { return header; }
+const char *ID3v1::Tag::GetTitle(void) const { return title; }
+const char *ID3v1::Tag::GetArtist(void) const { return artist; }
+const char *ID3v1::Tag::GetAlbum(void) const { return album; }
+const char *ID3v1::Tag::GetYear(void) const { return year; }
+const char *ID3v1::Tag::GetComment(void) const { return comment; }
+uint8_t ID3v1::Tag::GetTrack(void) const { return track; }
+uint8_t ID3v1::Tag::GetGenre(void) const { return genre; }
+
+//unsigned int ID3v1::Tag::GetTitleLength(void) { return ( strlen(title) <= 30 ) ? strlen(title) : 30; }
+size_t ID3v1::Tag::GetHeaderLength(void) const { return id3v1_strnlen(header, 3); }
+size_t ID3v1::Tag::GetTitleLength(void) const { return id3v1_strnlen(title, 30); }
+size_t ID3v1::Tag::GetArtistLength(void) const { return id3v1_strnlen(artist, 30); }
+size_t ID3v1::Tag::GetAlbumLength(void) const { return id3v1_strnlen(album, 30); }
+size_t ID3v1::Tag::GetYearLength(void) const { return id3v1_strnlen(year, 4); }
+size_t ID3v1::Tag::GetCommentLength(void) const { return id3v1_strnlen(comment, 30); }
+
+int ID3v1::Tag::Parse(const void *data, size_t len)
+{
+ if (len < 128)
+ return NErr_NeedMoreData;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, len);
+
+ // Get header
+ bytereader_read_n(&byte_reader, header, 3);
+
+ // Get title
+ bytereader_read_n(&byte_reader, title, 30);
+
+ // Get artist
+ bytereader_read_n(&byte_reader, artist, 30);
+
+ // Get album
+ bytereader_read_n(&byte_reader, album, 30);
+
+ // Get year
+ bytereader_read_n(&byte_reader, year, 4);
+
+ // Get comments
+ bytereader_read_n(&byte_reader, comment, 30);
+
+ // Get genre
+ genre = bytereader_read_u8(&byte_reader);
+
+ // Check for the presence of track # inside of the comments field at offset position 29 & 30
+ if (comment[28] == 0 && comment[29] != 0)
+ {
+ track = comment[29];
+ }
+ else
+ track = 0;
+
+ return NErr_Success;
+}
+
+/* copies source_bytes characters to destination, and fills up the remaining (up to destination_length) with null */
+static void id3v1_strncpyn(char *destination, size_t destination_length, const char *source, size_t source_bytes)
+{
+ // make sure we don't write too much
+ if (source_bytes > destination_length)
+ source_bytes = destination_length;
+
+ memcpy(destination, source, source_bytes);
+ memset(destination+source_bytes, 0, destination_length-source_bytes); // zero remainder of string
+}
+
+void ID3v1::Tag::SetTitle(const char *new_title, size_t length)
+{
+ id3v1_strncpyn(title, 30, new_title, length);
+}
+
+void ID3v1::Tag::SetArtist(const char *new_artist, size_t length)
+{
+ id3v1_strncpyn(artist, 30, new_artist, length);
+}
+
+void ID3v1::Tag::SetAlbum(const char *new_album, size_t length)
+{
+ id3v1_strncpyn(album, 30, new_album, length);
+}
+
+void ID3v1::Tag::SetYear(const char *new_year, size_t length)
+{
+ id3v1_strncpyn(year, 4, new_year, length);
+}
+
+void ID3v1::Tag::SetComment(const char *new_comment, size_t length)
+{
+ id3v1_strncpyn(comment, 28, new_comment, length);
+}
+
+void ID3v1::Tag::SetTrack(uint8_t new_track)
+{
+ track = new_track;
+}
+
+void ID3v1::Tag::SetGenre(uint8_t new_genre)
+{
+ genre = new_genre;
+}
+
+int ID3v1::Tag::Serialize(void *data, size_t len)
+{
+ if (len < 128)
+ return NErr_Insufficient;
+
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, len);
+
+ bytewriter_write_n(&byte_writer, header, 3);
+ bytewriter_write_n(&byte_writer, title, 30);
+ bytewriter_write_n(&byte_writer, artist, 30);
+ bytewriter_write_n(&byte_writer, album, 30);
+ bytewriter_write_n(&byte_writer, year, 4);
+ bytewriter_write_n(&byte_writer, comment, 28);
+ bytewriter_write_u8(&byte_writer, 0);
+ bytewriter_write_u8(&byte_writer, track);
+ bytewriter_write_u8(&byte_writer, genre);
+
+ return NErr_Success;
+}
diff --git a/Src/replicant/nsid3v1/tag.h b/Src/replicant/nsid3v1/tag.h
new file mode 100644
index 000000000..d54bdd03e
--- /dev/null
+++ b/Src/replicant/nsid3v1/tag.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "nsid3v1.h"
+
+class nstest;
+
+namespace ID3v1
+{
+ class Tag
+ {
+ friend class nstest;
+ public:
+ Tag();
+ int Parse(const void *data, size_t len);
+ void New(); // creates an empty (but valid) tag
+
+ // Member value getters
+ const char *GetHeader(void) const;
+ const char *GetTitle(void) const;
+ const char *GetArtist(void) const;
+ const char *GetAlbum(void) const;
+ const char *GetYear(void) const;
+ const char *GetComment(void) const;
+ uint8_t GetTrack(void) const;
+ uint8_t GetGenre(void) const;
+
+ // Member length getters
+ size_t GetHeaderLength(void) const;
+ size_t GetTitleLength(void) const;
+ size_t GetArtistLength(void) const;
+ size_t GetAlbumLength(void) const;
+ size_t GetYearLength(void) const;
+ size_t GetCommentLength(void) const;
+
+ void SetTitle(const char *title, size_t length);
+ void SetArtist(const char *artist, size_t length);
+ void SetAlbum(const char *album, size_t length);
+ void SetYear(const char *year, size_t length);
+ void SetComment(const char *comment, size_t length);
+ void SetTrack(uint8_t track);
+ void SetGenre(uint8_t genre);
+
+ int Serialize(void *data, size_t len);
+ protected:
+ char header[3];
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30]; // Bytes 29 & 30 can contain 0 & genre respectivly, ID3V1.1
+ uint8_t track;
+ uint8_t genre;
+ };
+}
diff --git a/Src/replicant/nsid3v2/extendedheader.cpp b/Src/replicant/nsid3v2/extendedheader.cpp
new file mode 100644
index 000000000..b0440ae73
--- /dev/null
+++ b/Src/replicant/nsid3v2/extendedheader.cpp
@@ -0,0 +1,108 @@
+#include "extendedheader.h"
+#include "util.h"
+#include <string.h>
+#include <stdlib.h>
+#include "foundation/error.h"
+
+ID3v2_21::ExtendedHeaderBase::ExtendedHeaderBase(const ID3v2::Header &_tagHeader) : tagHeader(_tagHeader)
+{
+ memset(&headerData, 0, sizeof(ExtendedHeaderData));
+ data = 0;
+ data_size = 0;
+}
+
+uint32_t ID3v2_21::ExtendedHeaderBase::Size() const
+{
+ return headerData.size;
+}
+
+int ID3v2_21::ExtendedHeaderBase::Parse(const void *_data, size_t len, size_t *bytes_read)
+{
+ if (len < SIZE)
+ return 1;
+
+ if (tagHeader.Unsynchronised())
+ {
+ *bytes_read = ID3v2::Util::UnsynchroniseTo(&headerData, _data, SIZE);
+ }
+ else
+ {
+ memcpy(&headerData, _data, SIZE);
+ *bytes_read = SIZE;
+ }
+
+ _data = (const uint8_t *)_data+SIZE;
+
+ /* read any data after the header */
+ data_size = Size();
+ if (data_size)
+ {
+ /* sanity check size */
+ if (tagHeader.Unsynchronised())
+ {
+ if (ID3v2::Util::UnsynchronisedInputSize(_data, data_size) > len)
+ return 1;
+ }
+ else if (data_size > len)
+ return 1;
+
+ /* allocate and read data */
+ data = malloc(data_size);
+ if (tagHeader.Unsynchronised())
+ {
+ *bytes_read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size);
+ }
+ else
+ {
+ memcpy(data, _data, data_size);
+ *bytes_read += data_size;
+ }
+ }
+
+ return 0;
+}
+
+/* === ID3v2.3 === */
+ID3v2_3::ExtendedHeader::ExtendedHeader(const ID3v2::Header &_tagHeader) : ID3v2_21::ExtendedHeaderBase(_tagHeader)
+{
+}
+
+/* === ID3v2.4 === */
+ID3v2_4::ExtendedHeader::ExtendedHeader(const ID3v2::Header &_tagHeader) : ID3v2_21::ExtendedHeaderBase(_tagHeader)
+{
+}
+
+uint32_t ID3v2_4::ExtendedHeader::Size() const
+{
+ return ID3v2::Util::Int28To32(headerData.size);
+}
+
+int ID3v2_4::ExtendedHeader::Parse(const void *_data, size_t len, size_t *bytes_read)
+{
+ if (len < SIZE)
+ return 1;
+
+ memcpy(&headerData, _data, SIZE);
+ *bytes_read = SIZE;
+
+
+ _data = (const uint8_t *)_data+SIZE;
+
+ /* read any data after the header */
+ data_size = Size();
+ if (data_size)
+ {
+ /* sanity check size */
+ if (data_size > len)
+ return 1;
+
+ /* allocate and read data */
+ data = malloc(data_size);
+ if (!data)
+ return NErr_OutOfMemory;
+ memcpy(data, _data, data_size);
+ *bytes_read += data_size;
+ }
+
+ return 0;
+}
diff --git a/Src/replicant/nsid3v2/extendedheader.h b/Src/replicant/nsid3v2/extendedheader.h
new file mode 100644
index 000000000..fe85b614d
--- /dev/null
+++ b/Src/replicant/nsid3v2/extendedheader.h
@@ -0,0 +1,52 @@
+#pragma once
+#include "header.h"
+
+namespace ID3v2_21
+{
+#pragma pack(push, 1)
+ struct ExtendedHeaderData
+ {
+ uint32_t size;
+ };
+#pragma pack(pop)
+
+ class ExtendedHeaderBase
+ {
+ public:
+ ExtendedHeaderBase(const ID3v2::Header &_tagHeader);
+ int Parse(const void *_data, size_t len, size_t *bytes_read);
+ enum
+ {
+ SIZE=4,
+ };
+ protected:
+ uint32_t Size() const;
+ void *data;
+ size_t data_size;
+ ExtendedHeaderData headerData;
+ const ID3v2::Header &tagHeader;
+ };
+}
+
+namespace ID3v2_3
+{
+ class ExtendedHeader : public ID3v2_21::ExtendedHeaderBase
+ {
+ public:
+ ExtendedHeader(const ID3v2::Header &_tagHeader);
+ };
+}
+
+namespace ID3v2_4
+{
+ class ExtendedHeader : public ID3v2_21::ExtendedHeaderBase
+ {
+ public:
+ ExtendedHeader(const ID3v2::Header &_tagHeader);
+ int Parse(const void *_data, size_t len, size_t *bytes_read);
+
+ protected:
+ uint32_t Size() const;
+
+ };
+}
diff --git a/Src/replicant/nsid3v2/frame.cpp b/Src/replicant/nsid3v2/frame.cpp
new file mode 100644
index 000000000..12cdd1c6e
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame.cpp
@@ -0,0 +1,786 @@
+#include "frame.h"
+#include "util.h"
+#ifdef _WIN32
+#include "zlib/zlib.h"
+#else
+#include "zlib/zlib.h"
+#endif
+#include "frames.h"
+#include <string.h>
+#include <stdlib.h>
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "foundation/error.h"
+#include "nsid3v2.h"
+
+/* === ID3v2 common === */
+ID3v2::Frame::Frame()
+{
+ data = 0;
+ data_size = 0;
+}
+
+ID3v2::Frame::~Frame()
+{
+ free(data);
+}
+
+int ID3v2::Frame::GetData(const void **_data, size_t *data_len) const
+{
+ if (data)
+ {
+ *_data = data;
+ *data_len = data_size;
+ return NErr_Success;
+ }
+ else
+ return NErr_NullPointer;
+}
+
+size_t ID3v2::Frame::GetDataSize() const
+{
+ return data_size;
+}
+
+int ID3v2::Frame::NewData(size_t new_len, void **_data, size_t *_data_len)
+{
+ // we DO NOT update the header, as its meant to hold the original data
+ void *new_data = realloc(data, new_len);
+ if (new_data)
+ {
+ data = new_data;
+ data_size = new_len;
+ *_data = data;
+ *_data_len = data_size;
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+bool ID3v2::Frame::Encrypted() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::Compressed() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::Grouped() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::ReadOnly() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::FrameUnsynchronised() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::DataLengthIndicated() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::TagAlterPreservation() const
+{
+ return false;
+}
+
+bool ID3v2::Frame::FileAlterPreservation() const
+{
+ return false;
+}
+
+static inline void Advance(const void *&data, size_t &len, size_t amount)
+{
+ data = (const uint8_t *)data + amount;
+ len -= amount;
+}
+
+static inline void AdvanceBoth(const void *&data, size_t &len, size_t &len2, size_t amount)
+{
+ data = (const uint8_t *)data + amount;
+ len -= amount;
+ len2 -= amount;
+}
+
+
+/* === ID3v2.2 === */
+ID3v2_2::Frame::Frame(const ID3v2::Header &_header, const int8_t *id, int flags) : header(_header, id, flags)
+{
+}
+
+ID3v2_2::Frame::Frame(const FrameHeader &_header) : header(_header)
+{
+}
+
+int ID3v2_2::Frame::Parse(const void *_data, size_t len, size_t *read)
+{
+ *read = 0;
+ data_size = header.FrameSize(); // size of frame AFTER re-synchronization
+
+ /* check to make sure that we have enough input data to read the data */
+ if (header.Unsynchronised())
+ {
+ /* this is tricky, because the stored size reflects after re-synchronization,
+ but the incoming data is unsynchronized */
+ if (ID3v2::Util::UnsynchronisedInputSize(_data, data_size) > len)
+ return 1;
+ }
+ else if (data_size > len)
+ return 1;
+
+ /* allocate memory (real data_size) */
+ data = malloc(data_size);
+ if (!data)
+ return 1;
+
+ /* === Read the data === */
+ if (header.Unsynchronised())
+ {
+ *read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size);
+ }
+ else // normal data
+ {
+ memcpy(data, _data, data_size);
+ *read += data_size;
+ }
+
+ return NErr_Success;
+}
+
+
+int ID3v2_2::Frame::SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const
+{
+ ID3v2_2::FrameHeader new_header(header, tag_header);
+ // TODO: for now, we're not going to deal with compression
+ new_header.SetSize(data_size);
+
+ uint32_t current_length=0;
+ new_header.SerializedSize(&current_length);
+
+ if (new_header.Unsynchronised())
+ {
+ current_length += ID3v2::Util::SynchronisedSize(data, data_size);
+ }
+ else
+ {
+ current_length += new_header.FrameSize();
+ }
+
+ *length = current_length;
+ return NErr_Success;
+}
+
+int ID3v2_2::Frame::Serialize(void *output, uint32_t *written, const ID3v2::Header &tag_header, int flags) const
+{
+ size_t current_length = FrameHeader::SIZE;
+ uint8_t *data_ptr = (uint8_t *)output;
+ ID3v2_2::FrameHeader new_header(header, tag_header);
+ new_header.SetSize(data_size);
+
+ // write frame header
+ new_header.Serialize(data_ptr);
+ data_ptr += FrameHeader::SIZE;
+ if (new_header.Unsynchronised())
+ {
+ current_length += ID3v2::Util::SynchroniseTo(data_ptr, data, data_size);
+ }
+ else
+ {
+ memcpy(data_ptr, data, data_size);
+ current_length += data_size;
+ }
+ *written = current_length;
+ return NErr_Success;
+}
+
+
+const int8_t *ID3v2_2::Frame::GetIdentifier() const
+{
+ return header.GetIdentifier();
+}
+
+
+/* === ID3v2.3 === */
+ID3v2_3::Frame::Frame(const ID3v2::Header &_header, const int8_t *id, int flags) : header(_header, id, flags)
+{
+}
+
+ID3v2_3::Frame::Frame(const FrameHeader &_header) : header(_header)
+{
+}
+
+/* helper function
+reads num_bytes from input into output, dealing with re-synchronization and length checking
+increments input pointer
+increments bytes_read value by number of input bytes read (different from num_bytes when data is unsynchronized
+decrements input_len by bytes read
+decrements output_len by bytes written
+*/
+bool ID3v2_3::Frame::ReadData(void *output, const void *&input, size_t &input_len, size_t &frame_len, size_t num_bytes, size_t *bytes_read) const
+{
+ /* verify that we have enough data in the frame */
+ if (num_bytes > frame_len)
+ return false;
+
+ /* verify that we have enough data in the buffer */
+ size_t bytes_to_read;
+ if (header.Unsynchronised())
+ bytes_to_read = ID3v2::Util::UnsynchronisedInputSize(input, num_bytes);
+ else
+ bytes_to_read = num_bytes;
+
+ if (bytes_to_read > input_len)
+ return false;
+
+ /* read data */
+ if (header.Unsynchronised())
+ {
+ *bytes_read += ID3v2::Util::SynchroniseTo(&output, input, num_bytes);
+ }
+ else
+ {
+ *bytes_read += num_bytes;
+ memcpy(output, input, num_bytes);
+ }
+
+ /* increment input pointer */
+ input = (const uint8_t *)input + bytes_to_read;
+
+ /* decrement sizes */
+ frame_len -= num_bytes;
+ input_len -= bytes_to_read;
+ return true;
+}
+
+/* benski> this function is a bit complex
+we have two things to worry about, and can have any combination of the two
+1) Is the data 'unsynchronized'
+2) Is the data compressed (zlib)
+
+we keep track of three sizes:
+len - number of bytes in input buffer
+data_size - number of bytes of output data buffer
+frame_size - number of bytes of data in frame AFTER re-synchronization
+
+frame_size==data_size when compression is OFF
+*/
+int ID3v2_3::Frame::Parse(const void *_data, size_t len, size_t *read)
+{
+ *read = 0;
+ size_t frame_size = header.FrameSize(); // size of frame AFTER re-synchronization
+
+ if (header.Compressed())
+ {
+ // read 4 bytes of decompressed size
+ uint8_t raw_size[4];
+ if (ReadData(raw_size, _data, len, frame_size, 4, read) == false)
+ return 1;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, raw_size, 4);
+
+ data_size = bytereader_read_u32_be(&byte_reader);
+ }
+
+ /* Check for group identity. If this exists, we'll store it separate from the raw data */
+ if (header.Grouped())
+ {
+ // read 1 byte for group identity
+ if (ReadData(&group_identity, _data, len, frame_size, 1, read) == false)
+ return 1;
+ }
+
+ if (!header.Compressed())
+ {
+ data_size = frame_size;
+ }
+
+ /* check to make sure that we have enough input data to read the data */
+ if (!header.Compressed() && header.Unsynchronised())
+ {
+ /* this is tricky, because the stored size reflects after re-synchronization,
+ but the incoming data is unsynchronized */
+ if (ID3v2::Util::UnsynchronisedInputSize(_data, data_size) > len)
+ return 1;
+ }
+ else if (frame_size > len)
+ return 1;
+
+ /* allocate memory (real data_size) */
+ data = malloc(data_size);
+ if (!data)
+ return NErr_OutOfMemory;
+
+ /* === Read the data === */
+ if (header.Compressed())
+ {
+ if (header.Unsynchronised()) // compressed AND unsynchronized.. what a pain!!
+ {
+ // TODO: combined re-synchronization + inflation
+ void *temp = malloc(frame_size);
+ if (!temp)
+ return NErr_OutOfMemory;
+
+ *read += ID3v2::Util::UnsynchroniseTo(temp, _data, frame_size);
+
+ uLongf uncompressedSize = data_size;
+ int ret = uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)temp, frame_size);
+ free(temp);
+ if (ret != Z_OK)
+ return 1;
+ }
+ else
+ {
+ uLongf uncompressedSize = data_size;
+ if (uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)_data, frame_size) != Z_OK)
+ return 1;
+ *read += frame_size;
+ }
+ }
+ else if (header.Unsynchronised())
+ {
+ *read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size);
+ }
+ else // normal data
+ {
+ memcpy(data, _data, data_size);
+ *read += data_size;
+ }
+
+ return NErr_Success;
+}
+
+int ID3v2_3::Frame::SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const
+{
+ ID3v2_3::FrameHeader new_header(header, tag_header);
+ // TODO: for now, we're not going to deal with compression
+ new_header.ClearCompressed();
+ new_header.SetSize(data_size);
+
+ uint32_t current_length=0;
+ new_header.SerializedSize(&current_length);
+
+ if (new_header.Unsynchronised())
+ {
+ if (new_header.Compressed())
+ {
+ uint8_t data_length[4];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data_length, 4);
+ bytewriter_write_u32_be(&byte_writer, data_size);
+ current_length += ID3v2::Util::SynchronisedSize(&data_length, 4);
+ }
+
+ if (new_header.Grouped())
+ current_length += ID3v2::Util::SynchronisedSize(&group_identity, 1);
+ current_length += ID3v2::Util::SynchronisedSize(data, data_size);
+ }
+ else
+ {
+ current_length += new_header.FrameSize();
+ }
+
+ *length = current_length;
+ return NErr_Success;
+}
+
+int ID3v2_3::Frame::Serialize(void *output, uint32_t *written, const ID3v2::Header &tag_header, int flags) const
+{
+ size_t current_length = FrameHeaderBase::SIZE;
+ uint8_t *data_ptr = (uint8_t *)output;
+ ID3v2_3::FrameHeader new_header(header, tag_header);
+ // TODO: for now, we're not going to deal with compression
+ new_header.ClearCompressed();
+ new_header.SetSize(data_size);
+
+ // write frame header
+ uint32_t header_size;
+ new_header.Serialize(data_ptr, &header_size);
+ data_ptr += header_size;
+ if (new_header.Unsynchronised())
+ {
+ if (new_header.Compressed())
+ {
+ uint8_t data_length[4];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data_length, 4);
+ bytewriter_write_u32_be(&byte_writer, data_size);
+ current_length += ID3v2::Util::SynchroniseTo(data_ptr, &data_length, 4);
+ data_ptr+=4;
+ }
+
+ if (new_header.Grouped())
+ current_length += ID3v2::Util::SynchroniseTo(data_ptr++, &group_identity, 1);
+ current_length += ID3v2::Util::SynchroniseTo(data_ptr, data, data_size);
+ }
+ else
+ {
+ if (new_header.Compressed())
+ {
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data_ptr, 4);
+ bytewriter_write_u32_be(&byte_writer, data_size);
+ data_ptr+=4;
+ }
+
+ if (new_header.Grouped())
+ {
+ *data_ptr++ = group_identity;
+ current_length++;
+ }
+ memcpy(data_ptr, data, data_size);
+ current_length += data_size;
+ }
+ *written = current_length;
+ return NErr_Success;
+}
+
+const int8_t *ID3v2_3::Frame::GetIdentifier() const
+{
+ return header.GetIdentifier();
+}
+
+bool ID3v2_3::Frame::Encrypted() const
+{
+ return header.Encrypted();
+}
+
+bool ID3v2_3::Frame::Compressed() const
+{
+ return header.Compressed();
+}
+
+bool ID3v2_3::Frame::Grouped() const
+{
+ return header.Grouped();
+}
+
+bool ID3v2_3::Frame::ReadOnly() const
+{
+ return header.ReadOnly();
+}
+
+bool ID3v2_3::Frame::TagAlterPreservation() const
+{
+ return header.TagAlterPreservation();
+}
+
+bool ID3v2_3::Frame::FileAlterPreservation() const
+{
+ return header.FileAlterPreservation();
+}
+
+
+/* === ID3v2.4 === */
+ID3v2_4::Frame::Frame(const ID3v2::Header &_header, const int8_t *id, int flags) : header(_header, id, flags)
+{
+}
+
+ID3v2_4::Frame::Frame(const FrameHeader &_header) : header(_header)
+{
+}
+
+/* helper function
+reads num_bytes from input into output, dealing with re-synchronization and length checking
+increments input pointer
+increments bytes_read value by number of input bytes read (different from num_bytes when data is unsynchronized
+decrements input_len by bytes read
+decrements output_len by bytes written
+*/
+bool ID3v2_4::Frame::ReadData(void *output, const void *&input, size_t &input_len, size_t &frame_len, size_t num_bytes, size_t *bytes_read) const
+{
+ /* verify that we have enough data in the frame */
+ if (num_bytes > frame_len)
+ return false;
+
+ /* verify that we have enough data in the buffer */
+ size_t bytes_to_read = num_bytes;
+
+ if (bytes_to_read > input_len)
+ return false;
+
+ /* read data */
+
+ *bytes_read += num_bytes;
+ memcpy(output, input, num_bytes);
+
+ /* increment input pointer */
+ input = (const uint8_t *)input + bytes_to_read;
+
+ /* decrement sizes */
+ frame_len -= num_bytes;
+ input_len -= bytes_to_read;
+ return true;
+}
+
+/* benski> this function is a bit complex
+we have two things to worry about, and can have any combination of the two
+1) Is the data 'unsynchronized'
+2) Is the data compressed (zlib)
+
+we keep track of three sizes:
+len - number of bytes in input buffer
+data_size - number of bytes of output data buffer
+frame_size - number of bytes of data in frame AFTER re-synchronization
+
+frame_size==data_size when compression is OFF
+*/
+int ID3v2_4::Frame::Parse(const void *_data, size_t len, size_t *read)
+{
+ *read = 0;
+ size_t frame_size = header.FrameSize();
+
+ // TODO: if frame_size >= 128, verify size. iTunes v2.4 parser bug ...
+
+
+ /* Check for group identity. If this exists, we'll store it separate from the raw data */
+ /* Note: ID3v2.4 puts group identity BEFORE data length indicator, where as v2.3 has it the other way */
+ if (header.Grouped())
+ {
+ // read 1 byte for group identity
+ if (ReadData(&group_identity, _data, len, frame_size, 1, read) == false)
+ return 1;
+ }
+
+ if (header.Compressed() || header.DataLengthIndicated())
+ {
+ // read 4 bytes of decompressed size
+ uint8_t raw_size[4];
+ if (ReadData(raw_size, _data, len, frame_size, 4, read) == false)
+ return 1;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, raw_size, 4);
+
+ data_size = bytereader_read_u32_be(&byte_reader);
+ }
+
+ if (!(header.Compressed() || header.DataLengthIndicated()))
+ {
+ data_size = frame_size;
+ }
+
+ /* check to make sure that we have enough input data to read the data */
+
+ if (frame_size > len)
+ return 1;
+
+ if (!header.Compressed() && header.Unsynchronised())
+ {
+ data_size = ID3v2::Util::UnsynchronisedOutputSize(_data, frame_size);
+ }
+
+ /* allocate memory (real data_size) */
+ data = malloc(data_size);
+ if (!data)
+ return NErr_OutOfMemory;
+
+ /* === Read the data === */
+ if (header.Compressed())
+ {
+ if (header.Unsynchronised()) // compressed AND unsynchronized.. what a pain!!
+ {
+ // TODO: combined re-synchronization + inflation
+ size_t sync_size = ID3v2::Util::UnsynchronisedOutputSize(_data, frame_size);
+ void *temp = malloc(sync_size);
+ if (!temp)
+ return NErr_OutOfMemory;
+
+ *read += ID3v2::Util::UnsynchroniseTo(temp, _data, sync_size);
+
+ uLongf uncompressedSize = data_size;
+ int ret = uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)temp, sync_size);
+ /* TODO: realloc and set data_size to uncompressedSize if uncompressedSize was actually lower */
+ free(temp);
+ if (ret != Z_OK)
+ return 1;
+ }
+ else
+ {
+ uLongf uncompressedSize = data_size;
+ if (uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)_data, frame_size) != Z_OK)
+ return 1;
+ /* TODO: realloc and set data_size to uncompressedSize if uncompressedSize was actually lower */
+ *read += frame_size;
+ }
+ }
+ else if (header.Unsynchronised())
+ {
+ *read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size);
+ }
+ else // normal data
+ {
+ memcpy(data, _data, data_size);
+ *read += data_size;
+ }
+
+ return 0;
+}
+
+int ID3v2_4::Frame::SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const
+{
+ ID3v2_4::FrameHeader new_header(header, tag_header);
+ // TODO: for now, we're not going to deal with compression
+ new_header.ClearCompressed();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ // TODO: this doesn't handle compression
+ if (new_header.Unsynchronised())
+ {
+ size_t unsynchronized_data_size = ID3v2::Util::SynchronisedSize(data, data_size);
+ new_header.SetSize(unsynchronized_data_size);
+ }
+ else
+ {
+ new_header.SetSize(data_size);
+ }
+
+
+ size_t current_length = ID3v2_4::FrameHeader::SIZE;
+
+ if (new_header.Unsynchronised())
+ {
+ if (new_header.DataLengthIndicated() || new_header.Compressed())
+ {
+ current_length += 4;
+ }
+
+ if (new_header.Grouped())
+ current_length += ID3v2::Util::SynchronisedSize(&group_identity, 1);
+ current_length += ID3v2::Util::SynchronisedSize(data, data_size);
+ }
+ else
+ {
+ current_length += new_header.FrameSize();
+ }
+
+ *length = current_length;
+ return NErr_Success;
+}
+
+int ID3v2_4::Frame::Serialize(void *output, uint32_t *written, const ID3v2::Header &tag_header, int flags) const
+{
+ size_t current_length = ID3v2_4::FrameHeader::SIZE;
+ uint8_t *data_ptr = (uint8_t *)output;
+ ID3v2_4::FrameHeader new_header(header, tag_header);
+ // TODO: for now, we're not going to deal with compression
+ new_header.ClearCompressed();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ // TODO: this doesn't handle compression
+ if (new_header.Unsynchronised())
+ {
+ size_t unsynchronized_data_size = ID3v2::Util::SynchronisedSize(data, data_size);
+ new_header.SetSize(unsynchronized_data_size);
+ }
+ else
+ {
+ new_header.SetSize(data_size);
+ }
+
+ // write frame header
+ uint32_t header_size;
+ new_header.Serialize(data_ptr, &header_size);
+ data_ptr += header_size;
+
+ if (new_header.Compressed() || new_header.DataLengthIndicated())
+ {
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data_ptr, 4);
+ bytewriter_write_u32_be(&byte_writer, ID3v2::Util::Int32To28(data_size));
+ data_ptr+=4;
+ current_length+=4;
+ }
+
+ if (new_header.Unsynchronised())
+ {
+
+ if (Grouped())
+ current_length += ID3v2::Util::SynchroniseTo(data_ptr++, &group_identity, 1);
+ current_length += ID3v2::Util::SynchroniseTo(data_ptr, data, data_size);
+ }
+ else
+ {
+
+ if (new_header.Grouped())
+ {
+ *data_ptr++ = group_identity;
+ current_length++;
+ }
+ memcpy(data_ptr, data, data_size);
+ current_length += data_size;
+ }
+ *written = current_length;
+ return NErr_Success;
+}
+
+const int8_t *ID3v2_4::Frame::GetIdentifier() const
+{
+ return header.GetIdentifier();
+}
+
+bool ID3v2_4::Frame::Encrypted() const
+{
+ return header.Encrypted();
+}
+
+bool ID3v2_4::Frame::Compressed() const
+{
+ return header.Compressed();
+}
+
+bool ID3v2_4::Frame::Grouped() const
+{
+ return header.Grouped();
+}
+
+bool ID3v2_4::Frame::ReadOnly() const
+{
+ return header.ReadOnly();
+}
+
+bool ID3v2_4::Frame::FrameUnsynchronised() const
+{
+ return header.FrameUnsynchronised();
+}
+
+bool ID3v2_4::Frame::DataLengthIndicated() const
+{
+ return header.DataLengthIndicated();
+}
+
+
+bool ID3v2_4::Frame::TagAlterPreservation() const
+{
+ return header.TagAlterPreservation();
+}
+
+bool ID3v2_4::Frame::FileAlterPreservation() const
+{
+ return header.FileAlterPreservation();
+}
diff --git a/Src/replicant/nsid3v2/frame.h b/Src/replicant/nsid3v2/frame.h
new file mode 100644
index 000000000..3cf5fe992
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame.h
@@ -0,0 +1,117 @@
+#pragma once
+#include "frameheader.h"
+#include "nu/PtrDeque.h"
+
+namespace ID3v2
+{
+ class Frame : public nu::PtrDequeNode
+ {
+ public:
+ virtual ~Frame();
+ int NewData(size_t new_len, void **data, size_t *data_len);
+ int GetData(const void **data, size_t *data_len) const;
+ size_t GetDataSize() const;
+ virtual const int8_t *GetIdentifier() const=0;
+ virtual unsigned int GetVersion() const=0;
+
+ virtual bool Encrypted() const;
+ virtual bool Compressed() const;
+ virtual bool Grouped() const;
+ virtual bool ReadOnly() const;
+ virtual bool FrameUnsynchronised() const;
+ virtual bool DataLengthIndicated() const;
+ virtual bool TagAlterPreservation() const;
+ virtual bool FileAlterPreservation() const;
+ protected:
+ Frame();
+ void *data;
+ size_t data_size; /* REAL size, might be different from header.headerData.size */
+ };
+}
+
+namespace ID3v2_2
+{
+ class Frame : public ID3v2::Frame
+ {
+ public:
+ Frame(const ID3v2::Header &_header, const int8_t *id, int flags); // creates an empty frame with a given ID
+ Frame(const ID3v2_2::FrameHeader &_header);
+ int Parse(const void *_data, size_t len, size_t *read);
+ int SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const;
+ // there is enough room guaranteed to be present because it will be checked with SerializedSize()
+ int Serialize(void *data, uint32_t *written, const ID3v2::Header &tag_header, int flags) const;
+ const int8_t *GetIdentifier() const;
+ unsigned int GetVersion() const { return 2; }
+ private:
+ ID3v2_2::FrameHeader header;
+ };
+}
+
+
+namespace ID3v2_3
+{
+ class Frame : public ID3v2::Frame
+ {
+ public:
+ Frame(const ID3v2::Header &_header, const int8_t *id, int flags); // creates an empty frame with a given ID
+ Frame(const ID3v2_3::FrameHeader &_header);
+ int Parse(const void *_data, size_t len, size_t *read);
+
+ int SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const;
+ // there is enough room guaranteed to be present because it will be checked with SerializedSize()
+ int Serialize(void *data, uint32_t *written, const ID3v2::Header &tag_header, int flags) const;
+
+ const int8_t *GetIdentifier() const;
+ unsigned int GetVersion() const { return 3; }
+ virtual bool Encrypted() const;
+ virtual bool Compressed() const;
+ virtual bool Grouped() const;
+ virtual bool ReadOnly() const;
+ virtual bool TagAlterPreservation() const;
+ virtual bool FileAlterPreservation() const;
+
+ private:
+ ID3v2_3::FrameHeader header;
+ uint8_t group_identity;
+ /* helper function
+ reads num_bytes from input into output, dealing with re-synchronization and length checking
+ increments bytes_read value by number of input bytes read (different from num_bytes when data is unsynchronized
+ decrements input_len by bytes read
+ decrements output_len by bytes written
+ */
+ bool ReadData(void *output, const void *&input, size_t &input_len, size_t &frame_len, size_t num_bytes, size_t *bytes_read) const;
+ };
+}
+
+namespace ID3v2_4
+{
+ class Frame : public ID3v2::Frame
+ {
+ public:
+ Frame(const ID3v2::Header &_header, const int8_t *id, int flags); // creates an empty frame with a given ID
+ Frame(const ID3v2_4::FrameHeader &_header);
+ int Parse(const void *_data, size_t len, size_t *read);
+ int SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const;
+ int Serialize(void *data, uint32_t *written, const ID3v2::Header &tag_header, int flags) const;
+ const int8_t *GetIdentifier() const;
+ unsigned int GetVersion() const { return 4; }
+ virtual bool Encrypted() const;
+ virtual bool Compressed() const;
+ virtual bool Grouped() const;
+ virtual bool ReadOnly() const;
+ virtual bool FrameUnsynchronised() const;
+ virtual bool DataLengthIndicated() const;
+ virtual bool TagAlterPreservation() const;
+ virtual bool FileAlterPreservation() const;
+ private:
+ ID3v2_4::FrameHeader header;
+ uint8_t group_identity;
+ /* helper function
+ reads num_bytes from input into output, dealing with re-synchronization and length checking
+ increments bytes_read value by number of input bytes read (different from num_bytes when data is unsynchronized
+ decrements input_len by bytes read
+ decrements output_len by bytes written
+ */
+ bool ReadData(void *output, const void *&input, size_t &input_len, size_t &frame_len, size_t num_bytes, size_t *bytes_read) const;
+ };
+}
diff --git a/Src/replicant/nsid3v2/frame_apic.cpp b/Src/replicant/nsid3v2/frame_apic.cpp
new file mode 100644
index 000000000..1276c29d5
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_apic.cpp
@@ -0,0 +1,247 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "nx/nxstring.h"
+
+struct ParsedPicture
+{
+ ParsedString mime;
+ uint8_t picture_type;
+ ParsedString description;
+ const void *picture_data;
+ size_t picture_byte_length;
+};
+
+static int ParsePicture(const void *data, size_t data_len, ParsedPicture &parsed)
+{
+ int ret;
+ if (data_len < 4)
+ return NErr_NeedMoreData;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ uint8_t encoding = bytereader_read_u8(&byte_reader);
+ /* mime type is always latin-1 */
+ ret = ParseNullTerminatedString(&byte_reader, 0, parsed.mime);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (bytereader_size(&byte_reader) < 2)
+ return NErr_NeedMoreData;
+
+ parsed.picture_type = bytereader_read_u8(&byte_reader);
+
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.description);
+ if (ret != NErr_Success)
+ return ret;
+
+ parsed.picture_data = bytereader_pointer(&byte_reader);
+ parsed.picture_byte_length = bytereader_size(&byte_reader);
+ return NErr_Success;
+}
+
+static int ParsePicturev2_2(const void *data, size_t data_len, ParsedPicture &parsed)
+{
+ int ret;
+ if (data_len < 6)
+ return NErr_NeedMoreData;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ uint8_t encoding = bytereader_read_u8(&byte_reader);
+
+ /* three byte "Image Format" field */
+ parsed.mime.encoding = 0;
+ parsed.mime.data = bytereader_pointer(&byte_reader);
+ parsed.mime.byte_length = 3;
+
+ bytereader_advance(&byte_reader, 3);
+
+ parsed.picture_type = bytereader_read_u8(&byte_reader);
+
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.description);
+ if (ret != NErr_Success)
+ return ret;
+
+ parsed.picture_data = bytereader_pointer(&byte_reader);
+ parsed.picture_byte_length = bytereader_size(&byte_reader);
+ return NErr_Success;
+}
+
+int NSID3v2_Frame_Picture_Get(const nsid3v2_frame_t f, nx_string_t *mime, uint8_t *picture_type, nx_string_t *description, const void **picture_data, size_t *length, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPicture parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0)
+ {
+ int ret;
+ if (frame->GetVersion() == 2)
+ ret = ParsePicturev2_2(data, data_len, parsed);
+ else
+ ret = ParsePicture(data, data_len, parsed);
+
+ if (ret == NErr_Success)
+ {
+ int ret;
+ if (mime)
+ {
+ ret = NXStringCreateFromParsedString(mime, parsed.mime, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ if (description)
+ {
+ ret = NXStringCreateFromParsedString(description, parsed.description, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ if (picture_type)
+ *picture_type = parsed.picture_type;
+
+ if (picture_data)
+ *picture_data = parsed.picture_data;
+ if (length)
+ *length = parsed.picture_byte_length;
+
+ return NErr_Success;
+ }
+ else
+ {
+ return ret;
+ }
+ }
+
+ }
+ return NErr_Empty;
+}
+
+/* ---------------- Setters ---------------- */
+static const char *GetMIME2_2(nx_string_t mime)
+{
+ if (!mime)
+ return "\0\0\0";
+
+ if (NXStringKeywordCompareWithCString(mime, "image/jpeg") == NErr_True || NXStringKeywordCompareWithCString(mime, "image/jpg") == NErr_True)
+ return "JPG";
+
+ if (NXStringKeywordCompareWithCString(mime, "image/png") == NErr_True)
+ return "PNG";
+
+ if (NXStringKeywordCompareWithCString(mime, "image/gif") == NErr_True)
+ return "GIF";
+
+ if (NXStringKeywordCompareWithCString(mime, "image/bmp") == NErr_True)
+ return "BMP";
+
+ return "\0\0\0";
+}
+
+int NSID3v2_Frame_Picture_Set(nsid3v2_frame_t f, nx_string_t mime, uint8_t picture_type, nx_string_t description, const void *picture_data, size_t length, int text_flags)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (frame)
+ {
+ if (frame->GetVersion() == 2)
+ {
+ /* first, we need to get the total encoded size */
+
+ size_t byte_count_description=0;
+ if (description)
+ {
+ int ret = NXStringGetBytesSize(&byte_count_description, description, nx_charset_latin1, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+ }
+
+ size_t total_size = 1 /* text encoding */
+ + 3 /* Image Format is 3 bytes in ID3v2.2*/
+ + 1 /* picture type */
+ + byte_count_description + 1 /* description + null terminator */
+ + length; /* picture length */
+
+ void *data;
+ size_t data_size;
+ int ret = frame->NewData(total_size, &data, &data_size);
+ if (ret != NErr_Success)
+ return ret;
+
+ size_t bytes_copied;
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, data_size);
+ bytewriter_write_u8(&byte_writer, 0); /* mark as Latin-1 */
+ bytewriter_write_n(&byte_writer, GetMIME2_2(mime), 3);
+ bytewriter_write_u8(&byte_writer, picture_type);
+ if (description)
+ {
+ NXStringGetBytes(&bytes_copied, description, bytewriter_pointer(&byte_writer), bytewriter_size(&byte_writer), nx_charset_latin1, 0);
+ bytewriter_advance(&byte_writer, bytes_copied);
+ }
+ bytewriter_write_u8(&byte_writer, 0); /* description null terminator */
+ bytewriter_write_n(&byte_writer, picture_data, length);
+ return NErr_Success;
+ }
+ else
+ {
+ /* first, we need to get the total encoded size */
+ size_t byte_count_mime=0;
+ if (mime)
+ {
+ int ret = NXStringGetBytesSize(&byte_count_mime, mime, nx_charset_latin1, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+ }
+
+ size_t byte_count_description=0;
+ if (description)
+ {
+ int ret = NXStringGetBytesSize(&byte_count_description, description, nx_charset_latin1, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+ }
+
+ size_t total_size = 1 /* text encoding */
+ + byte_count_mime + 1 /* mime + null terminator */
+ + 1 /* picture type */
+ + byte_count_description + 1 /* description + null terminator */
+ + length; /* picture length */
+
+ void *data;
+ size_t data_size;
+ int ret = frame->NewData(total_size, &data, &data_size);
+ if (ret != NErr_Success)
+ return ret;
+
+ size_t bytes_copied;
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, data_size);
+ bytewriter_write_u8(&byte_writer, 0); /* mark as Latin-1 */
+ if (mime)
+ {
+ NXStringGetBytes(&bytes_copied, mime, bytewriter_pointer(&byte_writer), bytewriter_size(&byte_writer), nx_charset_latin1, 0);
+ bytewriter_advance(&byte_writer, bytes_copied);
+ }
+ bytewriter_write_u8(&byte_writer, 0); /* MIME null terminator */
+ bytewriter_write_u8(&byte_writer, picture_type);
+ if (description)
+ {
+ NXStringGetBytes(&bytes_copied, description, bytewriter_pointer(&byte_writer), bytewriter_size(&byte_writer), nx_charset_latin1, 0);
+ bytewriter_advance(&byte_writer, bytes_copied);
+ }
+ bytewriter_write_u8(&byte_writer, 0); /* description null terminator */
+ bytewriter_write_n(&byte_writer, picture_data, length);
+ return NErr_Success;
+ }
+ }
+ return NErr_Empty;
+}
diff --git a/Src/replicant/nsid3v2/frame_comments.cpp b/Src/replicant/nsid3v2/frame_comments.cpp
new file mode 100644
index 000000000..d6e5b3318
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_comments.cpp
@@ -0,0 +1,185 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/AutoWide.h"
+#include "nx/nxstring.h"
+#include "nu/ByteWriter.h"
+
+struct ParsedComments
+{
+ char language[3];
+ ParsedString description;
+ ParsedString value;
+};
+
+static int ParseComments(const void *data, size_t data_len, ParsedComments &parsed)
+{
+ int ret;
+ if (data_len < 5)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ // Get encoding
+ uint8_t encoding = bytereader_read_u8(&byte_reader);
+ // Get language
+ for (int i = 0; i < 3; i++)
+ parsed.language[i] = bytereader_read_u8(&byte_reader);
+
+ // Get description
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.description);
+ if (ret != NErr_Success)
+ return ret;
+ // Get actual text value
+ ret = ParseFrameTerminatedString(&byte_reader, encoding, parsed.value);
+
+ return ret;
+}
+
+int NSID3v2_Tag_Comments_Find(const nsid3v2_tag_t t, const char *description, nsid3v2_frame_t *out_frame, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_COMMENTS);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedComments parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseComments(data, data_len, parsed) == NErr_Success && (!description || DescriptionMatches(parsed.description, description, text_flags)))
+ {
+ *out_frame = (nsid3v2_frame_t)frame;
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_Comments_Get(const nsid3v2_tag_t t, const char *description, char language[3], nx_string_t *value, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_COMMENTS);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedComments parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseComments(data, data_len, parsed) == NErr_Success && (!description || DescriptionMatches(parsed.description, description, text_flags)))
+ {
+ if (language)
+ memcpy(language, parsed.language, 3);
+ return NXStringCreateFromParsedString(value, parsed.value, text_flags);
+ }
+
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Frame_Comments_Get(const nsid3v2_frame_t f, nx_string_t *description, char language[3], nx_string_t *value, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedComments parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseComments(data, data_len, parsed) == NErr_Success)
+ {
+ if (language)
+ memcpy(language, parsed.language, 3);
+
+ int ret = NXStringCreateFromParsedString(value, parsed.value, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (description)
+ return NXStringCreateFromParsedString(description, parsed.description, text_flags);
+ else
+ return NErr_Success;
+ }
+
+ }
+ return NErr_Error;
+}
+/* ---------------- Setters ---------------- */
+int NSID3v2_Frame_Comments_Set(nsid3v2_frame_t f, const char *description, const char language[3], nx_string_t value, int text_flags)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (frame)
+ {
+ /* benski> for now, we're going to store UTF-16LE always. in the future, we'll add functions to NXString to determine a 'best' encoding */
+ size_t description_length=description?strlen(description):0;
+
+ size_t byte_count_value=0;
+ int ret = NXStringGetBytesSize(&byte_count_value, value, nx_charset_utf16le, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+
+ /* TODO: overflow check */
+ size_t total_size = 1 /* encoding */ + 3 /* language */ + 2 /* BOM for description */ + description_length*2 + 2 /* null separator */ + 2 /* BOM for value */ + byte_count_value;
+
+ void *data;
+ size_t data_len;
+ ret = frame->NewData(total_size, &data, &data_len);
+ if (ret != NErr_Success)
+ return ret;
+
+ size_t bytes_copied;
+
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, data_len);
+ bytewriter_write_u8(&byte_writer, 1); /* mark as UTF-16LE */
+ if (language)
+ bytewriter_write_n(&byte_writer, language, 3);
+ else
+ bytewriter_write_zero_n(&byte_writer, 3);
+ bytewriter_write_u16_le(&byte_writer, 0xFEFF); /* BOM for description */
+ for (size_t i=0;i<description_length;i++)
+ bytewriter_write_u16_le(&byte_writer, description[i]);
+ bytewriter_write_u16_le(&byte_writer, 0); /* NULL separator*/
+ bytewriter_write_u16_le(&byte_writer, 0xFEFF); /* BOM for value */
+ NXStringGetBytes(&bytes_copied, value, bytewriter_pointer(&byte_writer), bytewriter_size(&byte_writer), nx_charset_utf16le, 0);
+ return NErr_Success;
+ }
+ return NErr_Error;
+}
+
+int NSID3v2_Tag_Comments_Set(nsid3v2_tag_t t, const char *description, const char language[3], nx_string_t value, int text_flags)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_COMMENTS);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedComments parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseComments(data, data_len, parsed) == NErr_Success && (!description || DescriptionMatches(parsed.description, description, text_flags)))
+ {
+ break;
+ }
+
+ frame = tag->FindNextFrame(frame);
+ }
+
+ if (!frame)
+ {
+ frame = tag->NewFrame(NSID3V2_FRAME_COMMENTS, 0);
+ if (!frame)
+ return NErr_OutOfMemory;
+ tag->AddFrame(frame);
+ }
+
+ return NSID3v2_Frame_Comments_Set((nsid3v2_frame_t)frame, description, language, value, text_flags);
+}
diff --git a/Src/replicant/nsid3v2/frame_id.cpp b/Src/replicant/nsid3v2/frame_id.cpp
new file mode 100644
index 000000000..807d8cd62
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_id.cpp
@@ -0,0 +1,167 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "nx/nxstring.h"
+
+struct ParsedID
+{
+ ParsedString owner;
+ const void *identifier_data;
+ size_t identifier_byte_length;
+};
+
+static int ParseID(const void *data, size_t data_len, ParsedID &parsed)
+{
+ int ret;
+ if (data_len < 1)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ /* owner is always latin-1 */
+ ret = ParseNullTerminatedString(&byte_reader, 0, parsed.owner);
+ if (ret != NErr_Success)
+ return ret;
+ parsed.identifier_data = bytereader_pointer(&byte_reader);
+ parsed.identifier_byte_length = bytereader_size(&byte_reader);
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_ID_Find(const nsid3v2_tag_t t, const char *owner, nsid3v2_frame_t *out_frame, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_ID);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedID parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseID(data, data_len, parsed) == NErr_Success && (!owner || DescriptionMatches(parsed.owner, owner, text_flags)))
+ {
+ *out_frame = (nsid3v2_frame_t)frame;
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Frame_ID_Get(nsid3v2_frame_t f, nx_string_t *owner, const void **id_data, size_t *length, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedID parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseID(data, data_len, parsed) == NErr_Success)
+ {
+ if (owner)
+ {
+ int ret = NXStringCreateFromParsedString(owner, parsed.owner, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ *id_data = parsed.identifier_data;
+ *length = parsed.identifier_byte_length;
+
+ return NErr_Success;
+ }
+
+ }
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_ID_Get(const nsid3v2_tag_t t, const char *owner, const void **id_data, size_t *length, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_ID);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedID parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseID(data, data_len, parsed) == NErr_Success && (!owner || DescriptionMatches(parsed.owner, owner, text_flags)))
+ {
+ *id_data = parsed.identifier_data;
+ *length = parsed.identifier_byte_length;
+
+ return NErr_Success;
+ }
+
+ frame = tag->FindNextFrame(frame);
+ }
+ return NErr_Empty;
+}
+
+
+
+/* ---------------- Setters ---------------- */
+int NSID3v2_Frame_ID_Set(nsid3v2_frame_t f, const char *owner, const void *id_data, size_t length, int text_flags)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (frame)
+ {
+ size_t owner_length=owner?strlen(owner):0;
+
+ /* TODO: overflow check */
+ size_t total_size = owner_length + 1 + length;
+
+ void *data;
+ size_t data_len;
+ int ret = frame->NewData(total_size, &data, &data_len);
+ if (ret != NErr_Success)
+ return ret;
+
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, data_len);
+ bytewriter_write_n(&byte_writer, owner, owner_length);
+ bytewriter_write_u8(&byte_writer, 0); // write null terminator separately, in case owner is NULL
+ bytewriter_write_n(&byte_writer, id_data, length);
+
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_ID_Set(nsid3v2_tag_t t, const char *owner, const void *id_data, size_t length, int text_flags)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_ID);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedID parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseID(data, data_len, parsed) == NErr_Success && (!owner || DescriptionMatches(parsed.owner, owner, text_flags)))
+ {
+ break;
+ }
+
+ frame = tag->FindNextFrame(frame);
+ }
+
+ if (!frame)
+ {
+ frame = tag->NewFrame(NSID3V2_FRAME_ID, 0);
+ if (!frame)
+ return NErr_OutOfMemory;
+ tag->AddFrame(frame);
+ }
+
+ return NSID3v2_Frame_ID_Set((nsid3v2_frame_t)frame, owner, id_data, length, text_flags);
+}
diff --git a/Src/replicant/nsid3v2/frame_object.cpp b/Src/replicant/nsid3v2/frame_object.cpp
new file mode 100644
index 000000000..7a6500f36
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_object.cpp
@@ -0,0 +1,86 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+#if defined(_WIN32) && !defined(strcasecmp)
+#define strcasecmp _stricmp
+#else
+#include <strings.h>
+#endif
+
+struct ParsedObject
+{
+ ParsedString mime;
+ ParsedString filename;
+ ParsedString description;
+ const void *object_data;
+ size_t object_byte_length;
+};
+
+static int ParseObject(const void *data, size_t data_len, ParsedObject &parsed)
+{
+ int ret;
+ if (data_len == 0)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ /* encoding */
+ uint8_t encoding = bytereader_read_u8(&byte_reader);
+
+ /* read mime type (Always latin-1) */
+ ret = ParseNullTerminatedString(&byte_reader, 0, parsed.mime);
+ if (ret != NErr_Success)
+ return ret;
+
+ /* read filename */
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.filename);
+ if (ret != NErr_Success)
+ return ret;
+
+ /* read content description */
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.description);
+ if (ret != NErr_Success)
+ return ret;
+
+
+ parsed.object_data = bytereader_pointer(&byte_reader);
+ parsed.object_byte_length = bytereader_size(&byte_reader);
+
+ return NErr_Success;
+}
+
+int NSID3v2_Frame_Object_Get(const nsid3v2_frame_t f, nx_string_t *mime, nx_string_t *filename, nx_string_t *description, const void **out_data, size_t *length, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedObject parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseObject(data, data_len, parsed) == NErr_Success)
+ {
+ int ret = NXStringCreateFromParsedString(mime, parsed.mime, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateFromParsedString(filename, parsed.filename, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateFromParsedString(description, parsed.description, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+
+ *out_data = parsed.object_data;
+ *length = parsed.object_byte_length;
+
+ return NErr_Success;
+ }
+
+ }
+ return NErr_Empty;
+}
diff --git a/Src/replicant/nsid3v2/frame_popm.cpp b/Src/replicant/nsid3v2/frame_popm.cpp
new file mode 100644
index 000000000..e2a4f6a58
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_popm.cpp
@@ -0,0 +1,95 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+#if defined(_WIN32) && !defined(strcasecmp)
+#define strcasecmp _stricmp
+#else
+#include <strings.h>
+#endif
+
+struct ParsedPopularimeter
+{
+ ParsedString email;
+ uint8_t rating;
+ uint64_t playcount;
+};
+
+static int ParsePopularimeter(const void *data, size_t data_len, ParsedPopularimeter &parsed)
+{
+ int ret;
+ if (data_len < 6)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ /* read email (Always latin-1) */
+ ret = ParseNullTerminatedString(&byte_reader, 0, parsed.email);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (bytereader_size(&byte_reader) == 0)
+ return NErr_Insufficient;
+
+ parsed.rating = bytereader_read_u8(&byte_reader);
+
+ parsed.playcount=0;
+ while (bytereader_size(&byte_reader))
+ {
+
+ parsed.playcount <<= 8;
+ parsed.playcount |= bytereader_read_u8(&byte_reader);
+ }
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_Popularimeter_GetRatingPlaycount(const nsid3v2_tag_t t, const char *email, uint8_t *rating, uint64_t *playcount)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_POPULARIMETER);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPopularimeter parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePopularimeter(data, data_len, parsed) == NErr_Success)
+ {
+ if (!strcasecmp(email, (const char *)parsed.email.data))
+ {
+ *rating = parsed.rating;
+ *playcount = parsed.playcount;
+ return NErr_Success;
+ }
+
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Frame_Popularity_Get(nsid3v2_frame_t f, nx_string_t *email, uint8_t *rating, uint64_t *playcount, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPopularimeter parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePopularimeter(data, data_len, parsed) == NErr_Success)
+ {
+ int ret = NXStringCreateFromParsedString(email, parsed.email, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+ *rating = parsed.rating;
+ *playcount = parsed.playcount;
+ return NErr_Success;
+ }
+ }
+ return NErr_Empty;
+}
diff --git a/Src/replicant/nsid3v2/frame_private.cpp b/Src/replicant/nsid3v2/frame_private.cpp
new file mode 100644
index 000000000..f70b8fd3d
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_private.cpp
@@ -0,0 +1,61 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+#if defined(_WIN32) && !defined(strcasecmp)
+#define strcasecmp _stricmp
+#else
+#include <strings.h>
+#endif
+
+
+struct ParsedPrivate
+{
+ ParsedString owner;
+ const void *private_data;
+ size_t private_byte_length;
+};
+
+static int ParsePrivate(const void *data, size_t data_len, ParsedPrivate &parsed)
+{
+ if (data_len == 0)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ int ret = ParseNullTerminatedString(&byte_reader, 0, parsed.owner);
+ if (ret != NErr_Success)
+ return ret;
+
+ parsed.private_data = bytereader_pointer(&byte_reader);
+ parsed.private_byte_length = bytereader_size(&byte_reader);
+
+ return NErr_Success;
+}
+
+int NSID3v2_Frame_Private_Get(const nsid3v2_frame_t f, nx_string_t *description, const void **out_data, size_t *length)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPrivate parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePrivate(data, data_len, parsed) == NErr_Success)
+ {
+ int ret = NXStringCreateFromParsedString(description, parsed.owner, 0);
+ if (ret != NErr_Success)
+ return ret;
+
+ *out_data = parsed.private_data;
+ *length = parsed.private_byte_length;
+
+ return NErr_Success;
+ }
+
+ }
+ return NErr_Empty;
+}
diff --git a/Src/replicant/nsid3v2/frame_text.cpp b/Src/replicant/nsid3v2/frame_text.cpp
new file mode 100644
index 000000000..4e0260be4
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_text.cpp
@@ -0,0 +1,95 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+#include "nu/ByteWriter.h"
+#include "nsid3v2/frame_utils.h"
+
+static int ParseText(const void *data, size_t data_len, ParsedString &parsed)
+{
+ if (data_len == 0)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ return ParseFrameTerminatedString(&byte_reader, bytereader_read_u8(&byte_reader), parsed);
+}
+
+int NSID3v2_Frame_Text_Get(const nsid3v2_frame_t f, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedString parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseText(data, data_len, parsed) == NErr_Success)
+ {
+ return NXStringCreateFromParsedString(value, parsed, text_flags);
+ }
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_Text_Get(const nsid3v2_tag_t t, int frame_enum, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ const ID3v2::Frame *frame = tag->FindFirstFrame(frame_enum);
+ return NSID3v2_Frame_Text_Get((const nsid3v2_frame_t)frame, value, text_flags);
+}
+
+
+/* ---------------- Setters ---------------- */
+int NSID3v2_Frame_Text_Set(nsid3v2_frame_t f, nx_string_t value, int text_flags)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (frame)
+ {
+ /* benski> for now, we're going to store UTF-16LE always. in the future, we'll add functions to NXString to determine a 'best' encoding */
+ size_t byte_count=0;
+ int ret = NXStringGetBytesSize(&byte_count, value, nx_charset_utf16le, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+
+ void *data;
+ size_t data_len;
+ byte_count+=3; // need one byte for encoding type, two bytes for BOM
+ ret = frame->NewData(byte_count, &data, &data_len);
+ if (ret != NErr_Success)
+ return ret;
+
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, data_len);
+ bytewriter_write_u8(&byte_writer, 1); /* mark as UTF-16LE */
+ bytewriter_write_u16_le(&byte_writer, 0xFEFF); /* BOM */
+
+ size_t bytes_copied;
+ return NXStringGetBytes(&bytes_copied, value, bytewriter_pointer(&byte_writer), bytewriter_size(&byte_writer), nx_charset_utf16le, 0);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_Text_Set(nsid3v2_tag_t t, int frame_enum, nx_string_t value, int text_flags)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ ID3v2::Frame *frame = tag->FindFirstFrame(frame_enum);
+ if (!frame)
+ {
+ frame = tag->NewFrame(frame_enum, 0);
+ if (!frame)
+ return NErr_OutOfMemory;
+ tag->AddFrame(frame);
+ }
+
+ return NSID3v2_Frame_Text_Set((nsid3v2_frame_t)frame, value, text_flags);
+}
diff --git a/Src/replicant/nsid3v2/frame_url.cpp b/Src/replicant/nsid3v2/frame_url.cpp
new file mode 100644
index 000000000..307f6e44c
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_url.cpp
@@ -0,0 +1,42 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+#include "nsid3v2/frame_utils.h"
+
+static int ParseText(const void *data, size_t data_len, ParsedString &parsed)
+{
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ return ParseFrameTerminatedString(&byte_reader, 0, parsed);
+}
+
+int NSID3v2_Frame_URL_Get(const nsid3v2_frame_t f, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedString parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && ParseText(data, data_len, parsed) == NErr_Success)
+ {
+ return NXStringCreateFromParsedString(value, parsed, text_flags);
+ }
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_URL_Get(const nsid3v2_tag_t t, int frame_enum, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(frame_enum);
+ return NSID3v2_Frame_URL_Get((const nsid3v2_frame_t)frame, value, text_flags);
+}
+
+
diff --git a/Src/replicant/nsid3v2/frame_usertext.cpp b/Src/replicant/nsid3v2/frame_usertext.cpp
new file mode 100644
index 000000000..07d060396
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_usertext.cpp
@@ -0,0 +1,166 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "nx/nxstring.h"
+
+struct ParsedUserText
+{
+ ParsedString description;
+ ParsedString value;
+};
+
+static int ParseUserText(const void *data, size_t data_len, ParsedUserText &parsed)
+{
+ int ret;
+ if (data_len == 0)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ uint8_t encoding = bytereader_read_u8(&byte_reader);
+
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.description);
+ if (ret != NErr_Success)
+ return ret;
+
+ return ParseFrameTerminatedString(&byte_reader, encoding, parsed.value);
+}
+
+int NSID3v2_Tag_TXXX_Find(const nsid3v2_tag_t t, const char *description, nsid3v2_frame_t *out_frame, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_USER_TEXT);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedUserText parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseUserText(data, data_len, parsed) == NErr_Success && DescriptionMatches(parsed.description, description, text_flags))
+ {
+ *out_frame = (nsid3v2_frame_t)frame;
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_TXXX_Get(const nsid3v2_tag_t t, const char *description, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_USER_TEXT);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedUserText parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseUserText(data, data_len, parsed) == NErr_Success && DescriptionMatches(parsed.description, description, text_flags))
+ {
+ return NXStringCreateFromParsedString(value, parsed.value, text_flags);
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Frame_UserText_Get(const nsid3v2_frame_t f, nx_string_t *description, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedUserText parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseUserText(data, data_len, parsed) == NErr_Success)
+ {
+ int ret = NXStringCreateFromParsedString(value, parsed.value, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (description)
+ return NXStringCreateFromParsedString(description, parsed.description, text_flags);
+ else
+ return NErr_Success;
+ }
+
+ }
+ return NErr_Error;
+}
+/* ---------------- Setters ---------------- */
+int NSID3v2_Frame_UserText_Set(nsid3v2_frame_t f, const char *description, nx_string_t value, int text_flags)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (frame)
+ {
+ /* benski> for now, we're going to store UTF-16LE always. in the future, we'll add functions to NXString to determine a 'best' encoding */
+ size_t description_length=strlen(description);
+
+ size_t byte_count_value=0;
+ int ret = NXStringGetBytesSize(&byte_count_value, value, nx_charset_utf16le, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+
+ /* TODO: overflow check */
+ size_t total_size = 1 /* encoding */ + 2 /* BOM for description */ + description_length*2 + 2 /* null separator */ + 2 /* BOM for value */ + byte_count_value;
+
+ void *data;
+ size_t data_len;
+ ret = frame->NewData(total_size, &data, &data_len);
+ if (ret != NErr_Success)
+ return ret;
+
+ size_t bytes_copied;
+
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, data_len);
+ bytewriter_write_u8(&byte_writer, 1); /* mark as UTF-16LE */
+ bytewriter_write_u16_le(&byte_writer, 0xFEFF); /* BOM for description */
+ for (size_t i=0;i<description_length;i++)
+ bytewriter_write_u16_le(&byte_writer, description[i]);
+ bytewriter_write_u16_le(&byte_writer, 0); /* NULL separator*/
+ bytewriter_write_u16_le(&byte_writer, 0xFEFF); /* BOM for value */
+ NXStringGetBytes(&bytes_copied, value, bytewriter_pointer(&byte_writer), bytewriter_size(&byte_writer), nx_charset_utf16le, 0);
+ return NErr_Success;
+ }
+ return NErr_Error;
+}
+
+int NSID3v2_Tag_TXXX_Set(nsid3v2_tag_t t, const char *description, nx_string_t value, int text_flags)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+
+ ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_USER_TEXT);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedUserText parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseUserText(data, data_len, parsed) == NErr_Success && DescriptionMatches(parsed.description, description, text_flags))
+ {
+ break;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ if (!frame)
+ {
+ frame = tag->NewFrame(NSID3V2_FRAME_USER_TEXT, 0);
+ if (!frame)
+ return NErr_OutOfMemory;
+ tag->AddFrame(frame);
+ }
+
+ return NSID3v2_Frame_UserText_Set((nsid3v2_frame_t)frame, description, value, text_flags);
+}
diff --git a/Src/replicant/nsid3v2/frame_userurl.cpp b/Src/replicant/nsid3v2/frame_userurl.cpp
new file mode 100644
index 000000000..ca053012a
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_userurl.cpp
@@ -0,0 +1,78 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+#if defined(_WIN32) && !defined(strcasecmp)
+#define strcasecmp _stricmp
+#else
+#include <string.h>
+#endif
+
+struct ParsedUserURL
+{
+ ParsedString description;
+ ParsedString value;
+};
+
+static int ParseUserURL(const void *data, size_t data_len, ParsedUserURL &parsed)
+{
+ int ret;
+ if (data_len < 2)
+ return NErr_Insufficient;
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, data_len);
+
+ uint8_t encoding = bytereader_read_u8(&byte_reader);
+
+ ret = ParseNullTerminatedString(&byte_reader, encoding, parsed.description);
+ if (ret != NErr_Success)
+ return ret;
+
+ return ParseFrameTerminatedString(&byte_reader, 0, parsed.value);
+}
+
+int NSID3v2_Tag_WXXX_Get(const nsid3v2_tag_t t, const char *description, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_Empty;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_USER_TEXT);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedUserURL parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseUserURL(data, data_len, parsed) == NErr_Success && DescriptionMatches(parsed.description, description, text_flags))
+ {
+ return NXStringCreateFromParsedString(value, parsed.value, text_flags);
+ }
+
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Empty;
+}
+
+int NSID3v2_Frame_UserURL_Get(const nsid3v2_frame_t f, nx_string_t *description, nx_string_t *value, int text_flags)
+{
+ const ID3v2::Frame *frame = (const ID3v2::Frame *)f;
+ if (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedUserURL parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseUserURL(data, data_len, parsed) == NErr_Success)
+ {
+ int ret = NXStringCreateFromParsedString(value, parsed.value, text_flags);
+ if (ret != NErr_Success)
+ return ret;
+
+ return NXStringCreateFromParsedString(description, parsed.description, text_flags);
+ }
+
+ }
+ return NErr_Error;
+}
diff --git a/Src/replicant/nsid3v2/frame_utils.cpp b/Src/replicant/nsid3v2/frame_utils.cpp
new file mode 100644
index 000000000..6fe88686e
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_utils.cpp
@@ -0,0 +1,265 @@
+#include "frame_utils.h"
+#include "foundation/error.h"
+#include "nsid3v2/nsid3v2.h"
+#if defined(_WIN32) && !defined(strcasecmp)
+#define strcasecmp _stricmp
+#else
+#include <string.h>
+#endif
+
+int ParseDescription(const char *&str, size_t &data_len, size_t &str_cch)
+{
+ str_cch=0;
+ while (data_len && str[str_cch])
+ {
+ data_len--;
+ str_cch++;
+ }
+ if (!data_len)
+ return NErr_Error;
+
+ data_len--;
+ return NErr_Success;
+}
+
+int ParseDescription(const wchar_t *&str, size_t &data_len, size_t &str_cch, uint8_t &str_encoding)
+{
+ str_cch=0;
+ if (data_len > 2 && str[0] == 0xFFFE)
+ {
+ str_encoding=2;
+ str++;
+ str-=3;
+ }
+ else if (data_len > 2 && str[0] == 0xFEFF)
+ {
+ str_encoding=1;
+ str++;
+ data_len-=3;
+ }
+ else
+ {
+ data_len--;
+ }
+
+ while (data_len > 1 && str[str_cch])
+ {
+ data_len-=2;
+ str_cch++;
+ }
+
+ if (!data_len)
+ return NErr_Error;
+
+ data_len-=2;
+ return NErr_Success;
+}
+
+
+static void ParseBOM(bytereader_t reader, uint8_t *encoding, const uint8_t default_encoding)
+{
+ if (bytereader_size(reader) >= 2)
+ {
+ uint16_t bom = bytereader_show_u16_le(reader);
+ if (bom == 0xFFFE)
+ {
+ bytereader_advance(reader, 2);
+ *encoding=2;
+ }
+ else if (bom == 0xFEFF)
+ {
+ bytereader_advance(reader, 2);
+ *encoding=1;
+ }
+ else
+ {
+ *encoding=default_encoding;
+ }
+ }
+ else
+ {
+ *encoding=default_encoding;
+ }
+}
+
+int ParseNullTerminatedString(bytereader_t reader, uint8_t encoding, ParsedString &parsed)
+{
+ switch(encoding)
+ {
+ case 0: // ISO-8859-1
+ if (bytereader_size(reader) == 0)
+ return NErr_Insufficient;
+
+ parsed.encoding = 0;
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = 0;
+ while (bytereader_size(reader) && bytereader_read_u8(reader))
+ parsed.byte_length++;
+
+ return NErr_Success;
+ case 1: // UTF-16
+ if (bytereader_size(reader) < 2)
+ return NErr_Insufficient;
+
+ parsed.byte_length = 0;
+ ParseBOM(reader, &parsed.encoding, 1);
+ parsed.data = bytereader_pointer(reader);
+ while (bytereader_size(reader) && bytereader_read_u16_le(reader))
+ parsed.byte_length+=2;
+
+ return NErr_Success;
+ case 2: // UTF-16BE
+ if (bytereader_size(reader) < 2)
+ return NErr_Insufficient;
+
+ parsed.byte_length = 0;
+ ParseBOM(reader, &parsed.encoding, 2);
+ parsed.data = bytereader_pointer(reader);
+ while (bytereader_size(reader) && bytereader_read_u16_le(reader))
+ parsed.byte_length+=2;
+
+ return NErr_Success;
+ case 3: // UTF-8
+ if (bytereader_size(reader) == 0)
+ return NErr_Insufficient;
+
+ parsed.encoding = 3;
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = 0;
+
+ size_t start = bytereader_size(reader);
+#if 0 // TODO
+ /* check for UTF-8 BOM and skip it */
+ if (bytereader_size(reader) > 3 && bytereader_read_u8(reader) == 0xEF && bytereader_read_u8(reader) == 0xBB && bytereader_read_u8(reader) == 0xBF)
+ {
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = bytereader_size(reader);
+ }
+ else
+ {
+ /* no BOM but skip however far we read into the string */
+ size_t offset = start - bytereader_size(reader);
+ parsed.data = (const uint8_t *)parsed.data + offset;
+ parsed.byte_length -= offset;
+ }
+#endif
+ /* finish it up */
+ while (bytereader_size(reader) && bytereader_read_u8(reader))
+ parsed.byte_length++;
+
+ return NErr_Success;
+ }
+ return NErr_Unknown;
+}
+
+int ParseFrameTerminatedString(bytereader_t reader, uint8_t encoding, ParsedString &parsed)
+{
+ switch(encoding)
+ {
+ case 0: // ISO-8859-1
+ parsed.encoding = 0;
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = bytereader_size(reader);
+ return NErr_Success;
+ case 1: // UTF-16
+ if ((bytereader_size(reader) & 1) == 1)
+ return NErr_Error;
+ ParseBOM(reader, &parsed.encoding, 1);
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = bytereader_size(reader);
+ return NErr_Success;
+ case 2: // UTF-16BE
+ if ((bytereader_size(reader) & 1) == 1)
+ return NErr_Error;
+ ParseBOM(reader, &parsed.encoding, 2);
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = bytereader_size(reader);
+ return NErr_Success;
+ case 3: // UTF-8
+ parsed.encoding = 3;
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = bytereader_size(reader);
+ if (bytereader_size(reader) > 3 && bytereader_read_u8(reader) == 0xEF && bytereader_read_u8(reader) == 0xBB && bytereader_read_u8(reader) == 0xBF)
+ {
+ parsed.data = bytereader_pointer(reader);
+ parsed.byte_length = bytereader_size(reader);
+ }
+ return NErr_Success;
+ }
+ return NErr_Error;
+}
+
+int NXStringCreateFromParsedString(nx_string_t *value, ParsedString &parsed, int text_flags)
+{
+ switch(parsed.encoding)
+ {
+ case 0: // ISO-8859-1
+ if (parsed.byte_length == 0)
+ return NXStringCreateEmpty(value);
+ if (text_flags & NSID3V2_TEXT_SYSTEM)
+ return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_system);
+ else
+ return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_latin1);
+ case 1: // UTF-16
+ if (parsed.byte_length < 2)
+ return NXStringCreateEmpty(value);
+ return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_utf16le);
+ case 2: // UTF-16BE
+ if (parsed.byte_length < 2)
+ return NXStringCreateEmpty(value);
+ return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_utf16be);
+ case 3: // UTF-8
+ if (parsed.byte_length == 0)
+ return NXStringCreateEmpty(value);
+ return NXStringCreateWithBytes(value, parsed.data, parsed.byte_length, nx_charset_utf8);
+ default:
+ return NErr_Unknown;
+ }
+
+}
+
+bool DescriptionMatches(const ParsedString &parsed, const char *description, int text_flags)
+{
+ // see if our description matches
+ switch(parsed.encoding)
+ {
+ case 0: // ISO-8859-1
+ return !strcasecmp(description, (const char *)parsed.data);
+ case 1:
+ {
+ bytereader_value_t utf16;
+ bytereader_init(&utf16, parsed.data, parsed.byte_length);
+
+ while (*description && bytereader_size(&utf16))
+ {
+ if ((*description++ & ~0x20) != (bytereader_read_u16_le(&utf16) & ~0x20))
+ return false;
+ }
+
+ if (*description == 0 && bytereader_size(&utf16) == 0)
+ return true;
+ else
+ return false;
+ }
+
+ case 2:
+ {
+ bytereader_value_t utf16;
+ bytereader_init(&utf16, parsed.data, parsed.byte_length);
+
+ while (*description && bytereader_size(&utf16))
+ {
+ if ((*description++ & ~0x20) != (bytereader_read_u16_be(&utf16) & ~0x20))
+ return false;
+ }
+ if (*description == 0 && bytereader_size(&utf16) == 0)
+ return true;
+ else
+ return false;
+ }
+ case 3:
+ return !strcasecmp(description, (const char *)parsed.data);
+ }
+
+ return false;
+}
diff --git a/Src/replicant/nsid3v2/frame_utils.h b/Src/replicant/nsid3v2/frame_utils.h
new file mode 100644
index 000000000..1a24d5b71
--- /dev/null
+++ b/Src/replicant/nsid3v2/frame_utils.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "foundation/types.h"
+#include "nu/ByteReader.h"
+#include "nx/nxstring.h"
+
+
+/* updates str, data_len and str_cch */
+int ParseDescription(const char *&str, size_t &data_len, size_t &str_cch);
+int ParseDescription(const wchar_t *&str, size_t &data_len, size_t &str_cch, uint8_t &str_encoding);
+
+struct ParsedString
+{
+ uint8_t encoding; // 0 - iso-8859-1, 1 - UTF16LE, 2 - UTF16BE, 3 - UTF8
+ const void *data;
+ size_t byte_length;
+};
+
+int ParseNullTerminatedString(bytereader_t reader, uint8_t encoding, ParsedString &parsed);
+int ParseFrameTerminatedString(bytereader_t reader, uint8_t encoding, ParsedString &parsed);
+int NXStringCreateFromParsedString(nx_string_t *value, ParsedString &parsed, int text_flags);
+bool DescriptionMatches(const ParsedString &parsed, const char *description, int text_flags);
diff --git a/Src/replicant/nsid3v2/frameheader.cpp b/Src/replicant/nsid3v2/frameheader.cpp
new file mode 100644
index 000000000..3ffa63f9f
--- /dev/null
+++ b/Src/replicant/nsid3v2/frameheader.cpp
@@ -0,0 +1,403 @@
+#include "frameheader.h"
+#include "util.h"
+#include "values.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include <string.h>
+#include "foundation/error.h"
+/* === ID3v2 common === */
+ID3v2::FrameHeader::FrameHeader(const ID3v2::Header &_header) : tagHeader(_header)
+{
+}
+
+static bool CharOK(int8_t c)
+{
+ if (c >= '0' && c <= '9')
+ return true;
+
+ if (c >= 'A' && c <= 'Z')
+ return true;
+
+ return false;
+}
+
+/* === ID3v2.2 === */
+ID3v2_2::FrameHeader::FrameHeader(const ID3v2_2::FrameHeader &frame_header, const ID3v2::Header &_header) : ID3v2::FrameHeader(_header)
+{
+ frameHeaderData = frame_header.frameHeaderData;
+}
+
+ID3v2_2::FrameHeader::FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags) : ID3v2::FrameHeader(_header)
+{
+ memcpy(&frameHeaderData.id, id, 3);
+ frameHeaderData.id[3]=0;
+ memset(&frameHeaderData.size, 0, 3);
+}
+
+ID3v2_2::FrameHeader::FrameHeader(const ID3v2::Header &_header, const void *data) : ID3v2::FrameHeader(_header)
+{
+ char temp_data[FrameHeader::SIZE];
+ if (tagHeader.Unsynchronised())
+ {
+ ID3v2::Util::UnsynchroniseTo(temp_data, data, sizeof(temp_data));
+ data = temp_data;
+ }
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, FrameHeader::SIZE);
+
+ bytereader_read_n(&byte_reader, &frameHeaderData.id, 3);
+ frameHeaderData.id[3]=0;
+ bytereader_read_n(&byte_reader, &frameHeaderData.size, 3);
+}
+
+bool ID3v2_2::FrameHeader::IsValid() const
+{
+ if (CharOK(frameHeaderData.id[0])
+ && CharOK(frameHeaderData.id[1])
+ && CharOK(frameHeaderData.id[2]))
+ return true;
+
+ return false;
+}
+
+const int8_t *ID3v2_2::FrameHeader::GetIdentifier() const
+{
+ return frameHeaderData.id;
+}
+
+bool ID3v2_2::FrameHeader::Unsynchronised() const
+{
+ return tagHeader.Unsynchronised();
+}
+
+uint32_t ID3v2_2::FrameHeader::FrameSize() const
+{
+ return (frameHeaderData.size[0] << 16) | (frameHeaderData.size[1] << 8) | (frameHeaderData.size[2]);
+}
+
+void ID3v2_2::FrameHeader::SetSize(uint32_t data_size)
+{
+ frameHeaderData.size[0] = data_size >> 16;
+ frameHeaderData.size[1] = data_size >> 8;
+ frameHeaderData.size[2] = data_size;
+}
+
+int ID3v2_2::FrameHeader::SerializedSize(uint32_t *written) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t data[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, frameHeaderData.id, 3);
+ bytewriter_write_n(&byte_writer, frameHeaderData.size, 3);
+ *written = ID3v2::Util::SynchronisedSize(data, SIZE);
+ }
+ else
+ {
+ *written = SIZE;
+ }
+ return NErr_Success;
+}
+
+int ID3v2_2::FrameHeader::Serialize(void *data) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t temp[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, temp, SIZE);
+ bytewriter_write_n(&byte_writer, frameHeaderData.id, 3);
+ bytewriter_write_n(&byte_writer, frameHeaderData.size, 3);
+ ID3v2::Util::SynchroniseTo(data, temp, SIZE);
+ }
+ else
+ {
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, frameHeaderData.id, 3);
+ bytewriter_write_n(&byte_writer, frameHeaderData.size, 3);
+ }
+ return NErr_Success;
+}
+
+/* === ID3v2.3+ common === */
+ID3v2_3::FrameHeaderBase::FrameHeaderBase(const ID3v2_3::FrameHeaderBase &frame_header_base, const ID3v2::Header &_header) : ID3v2::FrameHeader(_header)
+{
+ memcpy(id, frame_header_base.id, 4);
+ size=frame_header_base.size;
+ flags[0] = frame_header_base.flags[0];
+ flags[1] = frame_header_base.flags[1];
+}
+
+ID3v2_3::FrameHeaderBase::FrameHeaderBase(const ID3v2::Header &_header) : ID3v2::FrameHeader(_header)
+{
+}
+
+ID3v2_3::FrameHeaderBase::FrameHeaderBase(const ID3v2::Header &_header, const int8_t *_id, int _flags) : ID3v2::FrameHeader(_header)
+{
+ memcpy(id, _id, 4);
+ size=0;
+ // TODO: flags
+ flags[0]=0;
+ flags[1]=0;
+}
+
+const int8_t *ID3v2_3::FrameHeaderBase::GetIdentifier() const
+{
+ return id;
+}
+
+
+bool ID3v2_3::FrameHeaderBase::IsValid() const
+{
+ if (CharOK(id[0])
+ && CharOK(id[1])
+ && CharOK(id[2])
+ && CharOK(id[3]))
+ return true;
+
+ return false;
+}
+
+
+
+/* === ID3v2.3 === */
+ID3v2_3::FrameHeader::FrameHeader(const ID3v2_3::FrameHeader &frame_header, const ID3v2::Header &tag_header) : ID3v2_3::FrameHeaderBase(frame_header, tag_header)
+{
+}
+
+ID3v2_3::FrameHeader::FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags) : ID3v2_3::FrameHeaderBase(_header, id, flags)
+{
+}
+
+ID3v2_3::FrameHeader::FrameHeader(const ID3v2::Header &_header, const void *data) : ID3v2_3::FrameHeaderBase(_header)
+{
+ char temp_data[FrameHeaderBase::SIZE];
+ if (tagHeader.Unsynchronised())
+ {
+ ID3v2::Util::UnsynchroniseTo(temp_data, data, sizeof(temp_data));
+ data = temp_data;
+ }
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, FrameHeaderBase::SIZE);
+
+ bytereader_read_n(&byte_reader, &id, 4);
+ size = bytereader_read_u32_be(&byte_reader);
+ bytereader_read_n(&byte_reader, &flags, 2);
+}
+
+int ID3v2_3::FrameHeaderBase::SerializedSize(uint32_t *written) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t data[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = ID3v2::Util::SynchronisedSize(data, SIZE);
+ }
+ else
+ {
+ *written = SIZE;
+ }
+ return NErr_Success;
+}
+
+int ID3v2_3::FrameHeaderBase::Serialize(void *data, uint32_t *written) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t temp[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, temp, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = ID3v2::Util::SynchroniseTo(data, temp, SIZE);
+ }
+ else
+ {
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = SIZE;
+ }
+ return NErr_Success;
+}
+
+uint32_t ID3v2_3::FrameHeader::FrameSize() const
+{
+ return size;
+}
+
+bool ID3v2_3::FrameHeader::ReadOnly() const
+{
+ return !!(flags[0] & (1<<5));
+}
+
+bool ID3v2_3::FrameHeader::Encrypted() const
+{
+ return !!(flags[1] & (1<<6));
+}
+
+bool ID3v2_3::FrameHeader::Unsynchronised() const
+{
+ return tagHeader.Unsynchronised();
+}
+
+bool ID3v2_3::FrameHeader::Grouped() const
+{
+ return !!(flags[1] & (1 << 5));
+}
+
+bool ID3v2_3::FrameHeader::Compressed() const
+{
+ return !!(flags[1] & (1 << 7));
+}
+
+bool ID3v2_3::FrameHeader::TagAlterPreservation() const
+{
+ return !!(flags[0] & (1<<7));
+}
+
+bool ID3v2_3::FrameHeader::FileAlterPreservation() const
+{
+ return !!(flags[0] & (1<<6));
+}
+
+void ID3v2_3::FrameHeader::ClearCompressed()
+{
+ flags[1] &= ~(1 << 7);
+}
+
+void ID3v2_3::FrameHeader::SetSize(uint32_t data_size)
+{
+ if (Compressed())
+ data_size+=4;
+ if (Grouped())
+ data_size++;
+ size = data_size;
+}
+
+/* === ID3v2.4 === */
+ID3v2_4::FrameHeader::FrameHeader(const ID3v2_4::FrameHeader &frame_header, const ID3v2::Header &tag_header) : ID3v2_3::FrameHeaderBase(frame_header, tag_header)
+{
+}
+
+ID3v2_4::FrameHeader::FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags) : ID3v2_3::FrameHeaderBase(_header, id, flags)
+{
+}
+
+ID3v2_4::FrameHeader::FrameHeader(const ID3v2::Header &_header, const void *data) : ID3v2_3::FrameHeaderBase(_header)
+{
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, FrameHeaderBase::SIZE);
+
+ bytereader_read_n(&byte_reader, &id, 4);
+ size = bytereader_read_u32_be(&byte_reader);
+ bytereader_read_n(&byte_reader, &flags, 2);
+}
+
+uint32_t ID3v2_4::FrameHeader::FrameSize() const
+{
+ // many programs write non-syncsafe sizes (iTunes is the biggest culprit)
+ // so we'll try to detect it. unfortunately this isn't foolproof
+ // ID3v2_4::Frame will have some additional checks
+ int mask = size & 0x80808080;
+ if (mask)
+ return size;
+ else
+ return ID3v2::Util::Int28To32(size);
+}
+
+bool ID3v2_4::FrameHeader::ReadOnly() const
+{
+ return !!(flags[0] & (1<<4));
+}
+
+bool ID3v2_4::FrameHeader::Encrypted() const
+{
+ return !!(flags[1] & (1<<3));
+}
+
+bool ID3v2_4::FrameHeader::Unsynchronised() const
+{
+ return tagHeader.Unsynchronised() || !!(flags[1] & (1 << 1));
+}
+
+bool ID3v2_4::FrameHeader::FrameUnsynchronised() const
+{
+ return !!(flags[1] & (1 << 1));
+}
+
+bool ID3v2_4::FrameHeader::DataLengthIndicated() const
+{
+ return !!(flags[1] & (1 << 0));
+}
+
+bool ID3v2_4::FrameHeader::Compressed() const
+{
+ return !!(flags[1] & (1 << 3));
+}
+
+bool ID3v2_4::FrameHeader::Grouped() const
+{
+ return !!(flags[1] & (1 << 6));
+}
+
+bool ID3v2_4::FrameHeader::TagAlterPreservation() const
+{
+ return !!(flags[0] & (1<<6));
+}
+
+bool ID3v2_4::FrameHeader::FileAlterPreservation() const
+{
+ return !!(flags[0] & (1<<5));
+}
+
+void ID3v2_4::FrameHeader::ClearUnsynchronized()
+{
+ flags[1] &= ~(1 << 1);
+}
+
+void ID3v2_4::FrameHeader::ClearCompressed()
+{
+ flags[1] &= ~(1 << 3);
+}
+
+void ID3v2_4::FrameHeader::SetSize(uint32_t data_size)
+{
+ if (Compressed() || DataLengthIndicated())
+ data_size+=4;
+ if (Grouped())
+ data_size++;
+ size = ID3v2::Util::Int32To28(data_size);
+}
+
+int ID3v2_4::FrameHeader::SerializedSize(uint32_t *written) const
+{
+ *written = SIZE;
+ return NErr_Success;
+}
+
+int ID3v2_4::FrameHeader::Serialize(void *data, uint32_t *written) const
+{
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = SIZE;
+ return NErr_Success;
+}
diff --git a/Src/replicant/nsid3v2/frameheader.h b/Src/replicant/nsid3v2/frameheader.h
new file mode 100644
index 000000000..bc518ff04
--- /dev/null
+++ b/Src/replicant/nsid3v2/frameheader.h
@@ -0,0 +1,124 @@
+#pragma once
+#include "foundation/types.h"
+#include "header.h"
+
+
+namespace ID3v2
+{
+ class FrameHeader
+ {
+ protected:
+ FrameHeader(const ID3v2::Header &_header);
+ const ID3v2::Header &tagHeader;
+ };
+}
+
+namespace ID3v2_2
+{
+
+ struct FrameHeaderData
+ {
+ int8_t id[4]; // ID3v2.2 uses 3 bytes but we add a NULL for the last to make it easier
+ uint8_t size[3]; // 24 bit size field
+ };
+
+ class FrameHeader : public ID3v2::FrameHeader
+ {
+ public:
+ FrameHeader(const ID3v2_2::FrameHeader &frame_header, const ID3v2::Header &_header);
+ FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags);
+ FrameHeader(const ID3v2::Header &_header, const void *data);
+ bool IsValid() const;
+ bool Unsynchronised() const;
+ uint32_t FrameSize() const;
+ const int8_t *GetIdentifier() const;
+ void SetSize(uint32_t data_size);
+ int SerializedSize(uint32_t *written) const;
+ int Serialize(void *data) const;
+ enum
+ {
+ SIZE=6,
+ };
+ private:
+ FrameHeaderData frameHeaderData;
+ };
+}
+
+namespace ID3v2_3
+{
+
+ class FrameHeaderBase : public ID3v2::FrameHeader
+ {
+ public:
+ int SerializedSize(uint32_t *written) const;
+ int Serialize(void *data, uint32_t *written) const;
+ bool IsValid() const;
+ const int8_t *GetIdentifier() const;
+ enum
+ {
+ SIZE=10,
+ };
+
+ protected:
+ FrameHeaderBase(const ID3v2_3::FrameHeaderBase &frame_header_base, const ID3v2::Header &_header);
+ FrameHeaderBase(const ID3v2::Header &_header);
+ FrameHeaderBase(const ID3v2::Header &_header, const int8_t *id, int flags);
+
+ int8_t id[4];
+ uint32_t size;
+ uint8_t flags[2];
+ };
+
+ class FrameHeader : public ID3v2_3::FrameHeaderBase
+ {
+ public:
+ FrameHeader(const ID3v2_3::FrameHeader &frame_header, const ID3v2::Header &_header);
+ FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags);
+ FrameHeader(const ID3v2::Header &_header, const void *data);
+
+ uint32_t FrameSize() const;
+ bool Encrypted() const;
+ bool Compressed() const;
+ bool Grouped() const;
+ bool ReadOnly() const;
+ bool Unsynchronised() const;
+ bool TagAlterPreservation() const;
+ bool FileAlterPreservation() const;
+
+ void ClearCompressed();
+ /* sets a new size, given a data size. this function might add additional size (grouped, compressed, etc) */
+ void SetSize(uint32_t data_size);
+
+ private:
+
+ };
+}
+
+namespace ID3v2_4
+{
+ class FrameHeader : public ID3v2_3::FrameHeaderBase
+ {
+ public:
+ FrameHeader(const ID3v2_4::FrameHeader &frame_header, const ID3v2::Header &_header);
+ FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags);
+ FrameHeader(const ID3v2::Header &_header, const void *data);
+ /* size to read from disk */
+ uint32_t FrameSize() const;
+ bool Encrypted() const;
+ bool Compressed() const;
+ bool Grouped() const;
+ bool ReadOnly() const;
+ bool Unsynchronised() const;
+ bool FrameUnsynchronised() const;
+ bool DataLengthIndicated() const;
+ bool TagAlterPreservation() const;
+ bool FileAlterPreservation() const;
+
+ void ClearUnsynchronized();
+ void ClearCompressed();
+ /* sets a new size, given a data size. this function might add additional size (grouped, compressed, etc) */
+ void SetSize(uint32_t data_size);
+ int SerializedSize(uint32_t *written) const;
+ int Serialize(void *data, uint32_t *written) const;
+ };
+}
diff --git a/Src/replicant/nsid3v2/frames.c b/Src/replicant/nsid3v2/frames.c
new file mode 100644
index 000000000..f64052c78
--- /dev/null
+++ b/Src/replicant/nsid3v2/frames.c
@@ -0,0 +1,61 @@
+#include "frames.h"
+
+/* this is a .c file to shut up GCC which doesn't like to convert from int8_t to char */
+
+/* order needs to match the enum in nsid3v2.h */
+
+const FrameID frame_ids[] =
+{
+ {FRAMEID("PIC"), FRAMEID("APIC"), FRAMEID("APIC")},
+ {FRAMEID("COM"), FRAMEID("COMM"), FRAMEID("COMM")},
+ {FRAMEID("POP"), FRAMEID("POPM"), FRAMEID("POPM")},
+ {FRAMEID("TAL"), FRAMEID("TALB"), FRAMEID("TALB")},
+ {FRAMEID("TBP"), FRAMEID("TBPM"), FRAMEID("TBPM")},
+ {FRAMEID("TCM"), FRAMEID("TCOM"), FRAMEID("TCOM")},
+ {FRAMEID("TCO"), FRAMEID("TCON"), FRAMEID("TCON")},
+ {FRAMEID("TCR"), FRAMEID("TCOP"), FRAMEID("TCOP")},
+ {FRAMEID("TDA"), FRAMEID("TDAT"), FRAMEID("TDAT")},
+ {FRAMEID("TDY"), FRAMEID("TDLY"), FRAMEID("TDLY")},
+ {FRAMEID(0), FRAMEID(0), FRAMEID("TDRC")},
+ {FRAMEID("TEN"), FRAMEID("TENC"), FRAMEID("TENC")},
+ {FRAMEID(0), FRAMEID("TEXT"), FRAMEID("TEXT")},
+ {FRAMEID("TFT"), FRAMEID("TFLT"), FRAMEID("TFLT")},
+ {FRAMEID("TIM"), FRAMEID("TIME"), FRAMEID("TIME")},
+ {FRAMEID("TT1"), FRAMEID("TIT1"), FRAMEID("TIT1")},
+ {FRAMEID("TT2"), FRAMEID("TIT2"), FRAMEID("TIT2")},
+ {FRAMEID("TT3"), FRAMEID("TIT3"), FRAMEID("TIT3")},
+ {FRAMEID("TKE"), FRAMEID("TKEY"), FRAMEID("TKEY")},
+ {FRAMEID("TLA"), FRAMEID("TLAN"), FRAMEID("TLAN")},
+ {FRAMEID("TLE"), FRAMEID("TLEN"), FRAMEID("TLEN")},
+ {FRAMEID("TMT"), FRAMEID("TMED"), FRAMEID("TMED")},
+ {FRAMEID(0), FRAMEID(0), FRAMEID("TMOO")},
+ {FRAMEID(0), FRAMEID("TOAL"), FRAMEID("TOAL")},
+
+ {FRAMEID("TOA"), FRAMEID("TOPE"), FRAMEID("TOPE")},
+
+ {FRAMEID("TP1"), FRAMEID("TPE1"), FRAMEID("TPE1")},
+ {FRAMEID("TP2"), FRAMEID("TPE2"), FRAMEID("TPE2")},
+ {FRAMEID("TP3"), FRAMEID("TPE3"), FRAMEID("TPE3")},
+ {FRAMEID("TP4"), FRAMEID("TPE4"), FRAMEID("TPE4")},
+ {FRAMEID("TPA"), FRAMEID("TPOS"), FRAMEID("TPOS")},
+ {FRAMEID("TPB"), FRAMEID("TPUB"), FRAMEID("TPUB")},
+ {FRAMEID("TRK"), FRAMEID("TRCK"), FRAMEID("TRCK")},
+ {FRAMEID("TRD"), FRAMEID("TRDA"), FRAMEID("TRDA")},
+
+ {FRAMEID("TRC"), FRAMEID("TSRC"), FRAMEID("TSRC")},
+ {FRAMEID("TSS"), FRAMEID("TSSE"), FRAMEID("TSSE")},
+ {FRAMEID("TYE"), FRAMEID("TYER"), FRAMEID("TYER")},
+
+ {FRAMEID("TXX"), FRAMEID("TXXX"), FRAMEID("TXXX")},
+ {FRAMEID("UFI"), FRAMEID("UFID"), FRAMEID("UFID")},
+
+};
+
+int ValidFrameID(int id)
+{
+ if (id < 0)
+ return 0;
+ if (id >= (sizeof(frame_ids) / sizeof(*frame_ids)))
+ return 0;
+ return 1;
+}
diff --git a/Src/replicant/nsid3v2/frames.h b/Src/replicant/nsid3v2/frames.h
new file mode 100644
index 000000000..d30ae26c5
--- /dev/null
+++ b/Src/replicant/nsid3v2/frames.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "foundation/types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FRAMEID(__frame_id) ((const int8_t*)__frame_id)
+
+typedef struct frameid_struct_t
+{
+ const int8_t *v2;
+ const int8_t *v3;
+ const int8_t *v4;
+} FrameID;
+
+extern const FrameID frame_ids[];
+
+int ValidFrameID(int frame_id);
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nsid3v2/header.cpp b/Src/replicant/nsid3v2/header.cpp
new file mode 100644
index 000000000..f006b238e
--- /dev/null
+++ b/Src/replicant/nsid3v2/header.cpp
@@ -0,0 +1,186 @@
+#include "header.h"
+#include "values.h"
+#include "util.h"
+#include <assert.h>
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include <string.h>
+#include "foundation/error.h"
+
+ID3v2::Header::Header()
+{
+ marker[0]=0;
+ marker[1]=0;
+ marker[2]=0;
+ version=0;
+ revision=0;
+ flags=0;
+ size=0;
+}
+
+ID3v2::Header::Header(uint8_t version, uint8_t revision)
+{
+ marker[0]='I';
+ marker[1]='D';
+ marker[2]='3';
+ this->version=version;
+ this->revision=revision;
+ this->flags=0;
+ this->size=0;
+}
+
+ID3v2::Header::Header(const void *data)
+{
+ Parse(data);
+}
+
+
+ID3v2::Header::Header(const ID3v2::Header *copy, uint32_t new_size)
+{
+ marker[0]=copy->marker[0];
+ marker[1]=copy->marker[1];
+ marker[2]=copy->marker[2];
+ version=copy->version;
+ revision=copy->revision;
+ flags=copy->flags;
+ size = Util::Int32To28(new_size);
+}
+
+void ID3v2::Header::Parse(const void *data)
+{
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, Header::SIZE);
+ bytereader_read_n(&byte_reader, &marker, 3);
+ version = bytereader_read_u8(&byte_reader);
+ revision = bytereader_read_u8(&byte_reader);
+ flags = bytereader_read_u8(&byte_reader);
+ size = bytereader_read_u32_be(&byte_reader);
+}
+
+int ID3v2::Header::Serialize(void *data)
+{
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, 10);
+ bytewriter_write_n(&byte_writer, marker, 3);
+ bytewriter_write_u8(&byte_writer, version);
+ bytewriter_write_u8(&byte_writer, revision);
+ bytewriter_write_u8(&byte_writer, flags);
+ bytewriter_write_u32_be(&byte_writer, size);
+ return NErr_Success;
+}
+
+int ID3v2::Header::SerializeAsHeader(void *data)
+{
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, 10);
+ bytewriter_write_n(&byte_writer, "ID3", 3);
+ bytewriter_write_u8(&byte_writer, version);
+ bytewriter_write_u8(&byte_writer, revision);
+ bytewriter_write_u8(&byte_writer, flags);
+ bytewriter_write_u32_be(&byte_writer, size);
+ return NErr_Success;
+}
+
+int ID3v2::Header::SerializeAsFooter(void *data)
+{
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, 10);
+ bytewriter_write_n(&byte_writer, "3DI", 3);
+ bytewriter_write_u8(&byte_writer, version);
+ bytewriter_write_u8(&byte_writer, revision);
+ bytewriter_write_u8(&byte_writer, flags);
+ bytewriter_write_u32_be(&byte_writer, size);
+ return NErr_Success;
+}
+
+bool ID3v2::Header::Valid() const
+{
+ if (marker[0] != 'I'
+ || marker[1] != 'D'
+ || marker[2] != '3')
+ return false;
+
+ if (!Values::KnownVersion(version, revision))
+ return false;
+
+ if (flags & ~Values::ValidHeaderMask(version, revision))
+ return false;
+
+ if (size & 0x80808080)
+ return false;
+
+ return true;
+}
+
+bool ID3v2::Header::FooterValid() const
+{
+ if (marker[0] != '3'
+ || marker[1] != 'D'
+ || marker[2] != 'I')
+ return false;
+
+ if (!Values::KnownVersion(version, revision))
+ return false;
+
+ if (flags & ~Values::ValidHeaderMask(version, revision))
+ return false;
+
+ if (size & 0x80808080)
+ return false;
+
+ return true;
+}
+
+uint32_t ID3v2::Header::TagSize() const
+{
+ uint32_t size = Util::Int28To32(this->size);
+ return size;
+}
+
+bool ID3v2::Header::HasExtendedHeader() const
+{
+ return !!(flags & ID3v2::Values::ExtendedHeaderFlag(version, revision));
+}
+
+uint8_t ID3v2::Header::GetVersion() const
+{
+ return version;
+}
+
+uint8_t ID3v2::Header::GetRevision() const
+{
+ return revision;
+}
+
+bool ID3v2::Header::Unsynchronised() const
+{
+ return !!(flags & (1 << 7));
+}
+
+bool ID3v2::Header::HasFooter() const
+{
+ if (version < 4)
+ return false;
+ return !!(flags & 0x10);
+}
+
+void ID3v2::Header::ClearExtendedHeader()
+{
+ flags &= ~ID3v2::Values::ExtendedHeaderFlag(version, revision);
+}
+
+void ID3v2::Header::ClearUnsynchronized()
+{
+ flags &= ~(1 << 7);
+}
+
+void ID3v2::Header::SetFooter(bool footer)
+{
+ if (version >= 4)
+ {
+ if (footer)
+ flags |= 0x10;
+ else
+ flags &= 0x10;
+ }
+}
diff --git a/Src/replicant/nsid3v2/header.h b/Src/replicant/nsid3v2/header.h
new file mode 100644
index 000000000..bb0099581
--- /dev/null
+++ b/Src/replicant/nsid3v2/header.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "foundation/types.h"
+
+namespace ID3v2
+{
+ class Header
+ {
+ public:
+ Header();
+ Header(uint8_t version, uint8_t revision);
+ Header(const void *data);
+ Header(const Header *copy, uint32_t new_size);
+ void Parse(const void *data);
+ int Serialize(void *data);
+ int SerializeAsHeader(void *data);
+ int SerializeAsFooter(void *data);
+ /* Does this seem like a valid ID3v2 header? */
+ bool Valid() const;
+ bool FooterValid() const;
+ /* how much space the tag occupies on disk */
+ uint32_t TagSize() const;
+ bool HasExtendedHeader() const;
+ uint8_t GetVersion() const;
+ uint8_t GetRevision() const;
+ bool Unsynchronised() const;
+ bool HasFooter() const;
+ void SetFooter(bool footer);
+ enum
+ {
+ SIZE=10,
+ };
+
+ void ClearExtendedHeader();
+ void ClearUnsynchronized();
+ private:
+
+ uint8_t marker[3];
+ uint8_t version;
+ uint8_t revision;
+ uint8_t flags;
+ uint32_t size;
+ };
+}
diff --git a/Src/replicant/nsid3v2/nsid3v2.h b/Src/replicant/nsid3v2/nsid3v2.h
new file mode 100644
index 000000000..b656c07d3
--- /dev/null
+++ b/Src/replicant/nsid3v2/nsid3v2.h
@@ -0,0 +1,190 @@
+#pragma once
+
+/* include any platform-specific stuff, first */
+#ifdef __ANDROID__
+#include "android/nsid3v2.h"
+#elif defined(_WIN32)
+#include "windows/nsid3v2.h"
+#elif defined(__linux__)
+#include "linux/nsid3v2.h"
+#elif defined(__APPLE__)
+#include "osx/nsid3v2.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ // must be exactly 10 bytes
+ NSID3V2_EXPORT int NSID3v2_Header_Valid(const void *header_data);
+ NSID3V2_EXPORT int NSID3v2_Header_FooterValid(const void *footer_data);
+ NSID3V2_EXPORT int NSID3v2_Header_Create(nsid3v2_header_t *header, const void *header_data, size_t header_len);
+ NSID3V2_EXPORT int NSID3v2_Header_New(nsid3v2_header_t *h, uint8_t version, uint8_t revision);
+
+ /* Creates from footer data instead of header data */
+ NSID3V2_EXPORT int NSID3v2_Header_FooterCreate(nsid3v2_header_t *footer, const void *footer_data, size_t footer_len);
+ NSID3V2_EXPORT int NSID3v2_Header_TagSize(const nsid3v2_header_t header, uint32_t *tag_size);
+ NSID3V2_EXPORT int NSID3v2_Header_HasFooter(const nsid3v2_header_t header);
+ NSID3V2_EXPORT int NSID3v2_Header_Destroy(nsid3v2_header_t header);
+
+ // currently, this function makes a copy of any necessary data. in the future, it would be better
+ // to make another version of this function that "borrows" your data
+ // if you can guarantee that the memory will outlive the nsid3v2_tag_t object
+ NSID3V2_EXPORT int NSID3v2_Tag_Create(nsid3v2_tag_t *tag, const nsid3v2_header_t header, const void *bytes, size_t bytes_len);
+ NSID3V2_EXPORT int NSID3v2_Tag_Destroy(nsid3v2_tag_t tag);
+
+ NSID3V2_EXPORT int NSID3v2_Tag_GetFrame(const nsid3v2_tag_t tag, int frame_enum, nsid3v2_frame_t *frame);
+ NSID3V2_EXPORT int NSID3v2_Tag_GetNextFrame(const nsid3v2_tag_t tag, const nsid3v2_frame_t start_frame, nsid3v2_frame_t *frame);
+ NSID3V2_EXPORT int NSID3v2_Tag_RemoveFrame(nsid3v2_tag_t tag, nsid3v2_frame_t frame);
+ NSID3V2_EXPORT int NSID3v2_Tag_CreateFrame(nsid3v2_tag_t tag, int frame_enum, int flags, nsid3v2_frame_t *frame);
+ NSID3V2_EXPORT int NSID3v2_Tag_AddFrame(nsid3v2_tag_t tag, nsid3v2_frame_t frame);
+ NSID3V2_EXPORT int NSID3v2_Tag_EnumerateFrame(const nsid3v2_tag_t tag, nsid3v2_frame_t position, nsid3v2_frame_t *frame);
+
+ NSID3V2_EXPORT int NSID3v2_Tag_GetInformation(nsid3v2_tag_t tag, uint8_t *version, uint8_t *revision, int *flags);
+
+ /*
+ get specific information out of a tag. returns the first one found that matches the requirements.
+ */
+ enum
+ {
+ NSID3V2_TEXT_SYSTEM=1, // use system code page instead of ISO-8859-1
+ };
+ NSID3V2_EXPORT int NSID3v2_Tag_TXXX_Find(const nsid3v2_tag_t tag, const char *description, nsid3v2_frame_t *out_frame, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_ID_Find(const nsid3v2_tag_t tag, const char *owner, nsid3v2_frame_t *out_frame, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_Comments_Find(const nsid3v2_tag_t tag, const char *description, nsid3v2_frame_t *out_frame, int text_flags);
+
+ NSID3V2_EXPORT int NSID3v2_Tag_Text_Get(const nsid3v2_tag_t tag, int frame_enum, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_TXXX_Get(const nsid3v2_tag_t tag, const char *description, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_Popularimeter_GetRatingPlaycount(const nsid3v2_tag_t tag, const char *email, uint8_t *rating, uint64_t *playcount);
+ NSID3V2_EXPORT int NSID3v2_Tag_Comments_Get(const nsid3v2_tag_t tag, const char *description, char language[3], nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_WXXX_Get(const nsid3v2_tag_t tag, const char *description, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_ID_Get(const nsid3v2_tag_t tag, const char *owner, const void **id_data, size_t *length, int text_flags);
+
+ NSID3V2_EXPORT int NSID3v2_Frame_GetInformation(nsid3v2_frame_t frame, int *type, int *flags);
+
+ NSID3V2_EXPORT int NSID3v2_Frame_Text_Get(const nsid3v2_frame_t frame, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_UserText_Get(const nsid3v2_frame_t frame, nx_string_t *description, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Private_Get(const nsid3v2_frame_t frame, nx_string_t *description, const void **data, size_t *length);
+ NSID3V2_EXPORT int NSID3v2_Frame_Object_Get(const nsid3v2_frame_t frame, nx_string_t *mime, nx_string_t *filename, nx_string_t *description, const void **out_data, size_t *length, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Popularity_Get(nsid3v2_frame_t frame, nx_string_t *email, uint8_t *rating, uint64_t *playcount, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Picture_Get(const nsid3v2_frame_t frame, nx_string_t *mime, uint8_t *picture_type, nx_string_t *description, const void **picture_data, size_t *length, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_ID_Get(nsid3v2_frame_t frame, nx_string_t *owner, const void **id_data, size_t *length, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Comments_Get(const nsid3v2_frame_t frame, nx_string_t *description, char language[3], nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_URL_Get(const nsid3v2_frame_t frame, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_UserURL_Get(const nsid3v2_frame_t frame, nx_string_t *description, nx_string_t *value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Binary_Get(nsid3v2_frame_t frame, const void **binary, size_t *length);
+
+ NSID3V2_EXPORT int NSID3v2_Frame_GetIdentifier(nsid3v2_frame_t frame, const char **identifier);
+
+ enum
+ {
+ SerializedSize_Padding = (0x1 << 0), // write 'padding_size' extra 0's to the file
+ SerializedSize_AbsoluteSize = (0x2 << 0), // 'padding_size' represents the final total tag size
+ SerializedSize_BlockSize = (0x3 << 0), // 'padding_size' represents a block size. the total tag size will be a multiple of 'padding_size'
+ SerializedSize_PaddingMask = (0x3 << 0),
+
+ // note that setting NEITHER of this will preserve whatever setting was originally used in the tag (or individual frames for ID3v2.4)
+ Serialize_Unsynchronize = (0x1 << 2), // force the tag to be unsynchronized, even if it wasn't originally
+ Serialize_NoUnsynchronize = (0x2 << 2), // disable all unsynchronization, even if the tag was originally unsynchronized
+ Serialize_UnsynchronizeMask = (0x3 << 2),
+
+ Serialize_NoCompression = (0x1 << 4), // disables all compression
+ Serialize_CompressionMask = (0x1 << 4),
+ };
+
+ NSID3V2_EXPORT int NSID3v2_Tag_SerializedSize(nsid3v2_tag_t tag, uint32_t *length, uint32_t padding_size, int flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_Serialize(nsid3v2_tag_t tag, void *data, uint32_t len, int flags);
+
+ NSID3V2_EXPORT int NSID3v2_Tag_Text_Set(nsid3v2_tag_t tag, int frame_enum, nx_string_t value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_TXXX_Set(nsid3v2_tag_t tag, const char *description, nx_string_t value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_Comments_Set(nsid3v2_tag_t tag, const char *description, const char language[3], nx_string_t value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Tag_ID_Set(nsid3v2_tag_t tag, const char *owner, const void *id_data, size_t length, int text_flags);
+
+ NSID3V2_EXPORT int NSID3v2_Frame_Text_Set(nsid3v2_frame_t frame, nx_string_t value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_UserText_Set(nsid3v2_frame_t frame, const char *description, nx_string_t value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Comments_Set(nsid3v2_frame_t frame, const char *description, const char language[3], nx_string_t value, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_ID_Set(nsid3v2_frame_t frame, const char *owner, const void *id_data, size_t length, int text_flags);
+ NSID3V2_EXPORT int NSID3v2_Frame_Picture_Set(nsid3v2_frame_t frame, nx_string_t mime, uint8_t picture_type, nx_string_t description, const void *picture_data, size_t length, int text_flags);
+ enum
+ {
+ NSID3V2_FRAME_PICTURE, // APIC
+ NSID3V2_FRAME_COMMENTS, // COMM
+ NSID3V2_FRAME_POPULARIMETER, // POPM
+ NSID3V2_FRAME_ALBUM, // TALB
+ NSID3V2_FRAME_BPM, // TBPM
+ NSID3V2_FRAME_COMPOSER, // TCOM
+ NSID3V2_FRAME_CONTENTTYPE, // TCON
+ NSID3V2_FRAME_COPYRIGHT, // TCOP
+ NSID3V2_FRAME_DATE, // TDAT
+ NSID3V2_FRAME_PLAYLISTDELAY, // TDLY
+ NSID3V2_FRAME_RECORDINGTIME, // TDRC
+ NSID3V2_FRAME_ENCODEDBY, // TENC
+ NSID3V2_FRAME_LYRICIST, // TEXT
+ NSID3V2_FRAME_FILETYPE, // TFLT
+ NSID3V2_FRAME_TIME, // TIME
+ NSID3V2_FRAME_CONTENTGROUP, // TIT1
+ NSID3V2_FRAME_TITLE, // TIT2
+ NSID3V2_FRAME_SUBTITLE, // TIT3
+ NSID3V2_FRAME_KEY, // TKEY
+ NSID3V2_FRAME_LANGUAGE, // TLAN
+ NSID3V2_FRAME_LENGTH, // TLEN
+ NSID3V2_FRAME_MEDIATYPE, // TMED
+ NSID3V2_FRAME_MOOD, // TMOO
+ NSID3V2_FRAME_ORIGINALALBUM, // TOAL
+
+ NSID3V2_FRAME_ORIGINALARTIST, // TOPE
+
+ NSID3V2_FRAME_LEADARTIST, // TPE1
+ NSID3V2_FRAME_BAND, // TPE2
+ NSID3V2_FRAME_CONDUCTOR, // TPE3
+ NSID3V2_FRAME_REMIXER, // TPE4
+ NSID3V2_FRAME_PARTOFSET, // TPOS
+ NSID3V2_FRAME_PUBLISHER, // TPUB
+ NSID3V2_FRAME_TRACK, // TRCK
+ NSID3V2_FRAME_RECORDINGDATES, // TRDA
+
+ NSID3V2_FRAME_ISRC, // TSRC
+ NSID3V2_FRAME_ENCODERSETTINGS, // TSSE
+ NSID3V2_FRAME_YEAR, // TYER
+
+ NSID3V2_FRAME_USER_TEXT, // TXXX
+
+ NSID3V2_FRAME_ID, // UFID
+
+ };
+
+ /* frame types */
+ enum
+ {
+ NSID3V2_FRAMETYPE_UNKNOWN,
+ NSID3V2_FRAMETYPE_TEXT,
+ NSID3V2_FRAMETYPE_USERTEXT,
+ NSID3V2_FRAMETYPE_COMMENTS,
+ NSID3V2_FRAMETYPE_URL,
+ NSID3V2_FRAMETYPE_USERURL,
+ NSID3V2_FRAMETYPE_PRIVATE,
+ NSID3V2_FRAMETYPE_OBJECT,
+ NSID3V2_FRAMETYPE_POPULARITY,
+ NSID3V2_FRAMETYPE_PICTURE,
+ NSID3V2_FRAMETYPE_ID,
+ };
+
+ /* these DO NOT map to the actual flag bitmasks, they are only for API usage! */
+ enum
+ {
+ NSID3V2_TAGFLAG_EXTENDED_HEADER = 1<<1,
+ NSID3V2_TAGFLAG_UNSYNCHRONIZED = 1<<2,
+ NSID3V2_TAGFLAG_HASFOOTER = 1<<3,
+
+ NSID3V2_FRAMEFLAG_TAG_ALTER_PRESERVATION = 1<<1,
+ NSID3V2_FRAMEFLAG_FILE_ALTER_PRESERVATION = 1<<2,
+ NSID3V2_FRAMEFLAG_ENCRYPTED = 1<<3,
+ NSID3V2_FRAMEFLAG_COMPRESSED = 1<<4,
+ NSID3V2_FRAMEFLAG_GROUPED = 1<<5,
+ NSID3V2_FRAMEFLAG_READONLY =1<<6,
+ NSID3V2_FRAMEFLAG_UNSYNCHRONIZED =1<<7,
+ NSID3V2_FRAMEFLAG_DATALENGTHINDICATED =1<<8,
+ };
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nsid3v2/nsid3v2.sln b/Src/replicant/nsid3v2/nsid3v2.sln
new file mode 100644
index 000000000..9e9f4b160
--- /dev/null
+++ b/Src/replicant/nsid3v2/nsid3v2.sln
@@ -0,0 +1,44 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsid3v2", "nsid3v2.vcxproj", "{32025DF9-ACB0-435E-8D51-743719818799}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {32025DF9-ACB0-435E-8D51-743719818799}.Debug|Win32.ActiveCfg = Debug|Win32
+ {32025DF9-ACB0-435E-8D51-743719818799}.Debug|Win32.Build.0 = Debug|Win32
+ {32025DF9-ACB0-435E-8D51-743719818799}.Debug|x64.ActiveCfg = Debug|x64
+ {32025DF9-ACB0-435E-8D51-743719818799}.Debug|x64.Build.0 = Debug|x64
+ {32025DF9-ACB0-435E-8D51-743719818799}.Release|Win32.ActiveCfg = Release|Win32
+ {32025DF9-ACB0-435E-8D51-743719818799}.Release|Win32.Build.0 = Release|Win32
+ {32025DF9-ACB0-435E-8D51-743719818799}.Release|x64.ActiveCfg = Release|x64
+ {32025DF9-ACB0-435E-8D51-743719818799}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EE7C04E4-0AAC-4AB2-8176-253E6622DB09}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nsid3v2/nsid3v2.vcxproj b/Src/replicant/nsid3v2/nsid3v2.vcxproj
new file mode 100644
index 000000000..506667e41
--- /dev/null
+++ b/Src/replicant/nsid3v2/nsid3v2.vcxproj
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{32025DF9-ACB0-435E-8D51-743719818799}</ProjectGuid>
+ <RootNamespace>nsid3v2</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x86_Debug\</OutDir>
+ <IntDir>x86_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x64_Debug\</OutDir>
+ <IntDir>x64_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x86_Release\</OutDir>
+ <IntDir>x86_Release\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x64_Release\</OutDir>
+ <IntDir>x64_Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\nu\nu.vcxproj">
+ <Project>{efc75a79-269f-44fc-bac5-d7d4fd4ec92c}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="extendedheader.cpp" />
+ <ClCompile Include="frame.cpp" />
+ <ClCompile Include="frameheader.cpp" />
+ <ClCompile Include="frames.c" />
+ <ClCompile Include="frame_apic.cpp" />
+ <ClCompile Include="frame_comments.cpp" />
+ <ClCompile Include="frame_id.cpp" />
+ <ClCompile Include="frame_object.cpp" />
+ <ClCompile Include="frame_popm.cpp" />
+ <ClCompile Include="frame_private.cpp" />
+ <ClCompile Include="frame_text.cpp" />
+ <ClCompile Include="frame_url.cpp" />
+ <ClCompile Include="frame_usertext.cpp" />
+ <ClCompile Include="frame_userurl.cpp" />
+ <ClCompile Include="frame_utils.cpp" />
+ <ClCompile Include="header.cpp" />
+ <ClCompile Include="tag.cpp" />
+ <ClCompile Include="util.cpp" />
+ <ClCompile Include="values.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="extendedheader.h" />
+ <ClInclude Include="frame.h" />
+ <ClInclude Include="frameheader.h" />
+ <ClInclude Include="frames.h" />
+ <ClInclude Include="frame_utils.h" />
+ <ClInclude Include="header.h" />
+ <ClInclude Include="nsid3v2.h" />
+ <ClInclude Include="precomp.h" />
+ <ClInclude Include="tag.h" />
+ <ClInclude Include="util.h" />
+ <ClInclude Include="values.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nsid3v2/nsid3v2_common.cpp b/Src/replicant/nsid3v2/nsid3v2_common.cpp
new file mode 100644
index 000000000..e1e53e5df
--- /dev/null
+++ b/Src/replicant/nsid3v2/nsid3v2_common.cpp
@@ -0,0 +1,366 @@
+#include "nsid3v2/nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include <string.h> // for memcmp
+#include <new>
+
+int NSID3v2_Header_Valid(const void *header_data)
+{
+ ID3v2::Header header(header_data);
+ if (header.Valid())
+ return NErr_Success;
+ else
+ return NErr_False;
+}
+
+int NSID3v2_Header_FooterValid(const void *footer_data)
+{
+ ID3v2::Header footer(footer_data);
+ if (footer.FooterValid())
+ return NErr_Success;
+ else
+ return NErr_False;
+}
+
+int NSID3v2_Header_Create(nsid3v2_header_t *h, const void *header_data, size_t header_len)
+{
+ if (header_len < 10)
+ return NErr_NeedMoreData;
+
+ ID3v2::Header header(header_data);
+ if (!header.Valid())
+ return NErr_Error;
+
+ nsid3v2_header_t new_header = (nsid3v2_header_t)new (std::nothrow) ID3v2::Header(header);
+ if (!new_header)
+ return NErr_OutOfMemory;
+ *h = new_header;
+ return NErr_Success;
+}
+
+int NSID3v2_Header_New(nsid3v2_header_t *h, uint8_t version, uint8_t revision)
+{
+ nsid3v2_header_t new_header = (nsid3v2_header_t)new (std::nothrow) ID3v2::Header(version, revision);
+ if (!new_header)
+ return NErr_OutOfMemory;
+ *h = new_header;
+ return NErr_Success;
+}
+
+int NSID3v2_Header_FooterCreate(nsid3v2_header_t *f, const void *footer_data, size_t footer_len)
+{
+ if (footer_len < 10)
+ return NErr_NeedMoreData;
+
+ ID3v2::Header footer(footer_data);
+ if (!footer.FooterValid())
+ return NErr_Error;
+
+ nsid3v2_header_t new_header = (nsid3v2_header_t)new (std::nothrow) ID3v2::Header(footer);
+ if (!new_header)
+ return NErr_OutOfMemory;
+ *f = new_header;
+ return NErr_Success;
+}
+
+int NSID3v2_Header_TagSize(const nsid3v2_header_t h, uint32_t *tag_size)
+{
+ const ID3v2::Header *header = (const ID3v2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ *tag_size = header->TagSize();
+ return NErr_Success;
+}
+
+int NSID3v2_Header_HasFooter(const nsid3v2_header_t h)
+{
+ const ID3v2::Header *header = (const ID3v2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ if (header->HasFooter())
+ return NErr_Success;
+ else
+ return NErr_False;
+}
+
+int NSID3v2_Header_Destroy(nsid3v2_header_t h)
+{
+ const ID3v2::Header *header = (const ID3v2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ delete header;
+ return NErr_Success;
+}
+
+/*
+================== Tag ==================
+= =
+=========================================
+*/
+
+int NSID3v2_Tag_Create(nsid3v2_tag_t *t, const nsid3v2_header_t h, const void *bytes, size_t bytes_len)
+{
+ const ID3v2::Header *header = (const ID3v2::Header *)h;
+ if (!header)
+ return NErr_NullPointer;
+
+ switch(header->GetVersion())
+ {
+ case 2:
+ {
+ ID3v2_2::Tag *tag = new (std::nothrow) ID3v2_2::Tag(*header);
+ if (!tag)
+ return NErr_OutOfMemory;
+ tag->Parse(bytes, bytes_len);
+ *t = (nsid3v2_tag_t)tag;
+ return NErr_Success;
+ }
+ case 3:
+ {
+ ID3v2_3::Tag *tag = new (std::nothrow) ID3v2_3::Tag(*header);
+ if (!tag)
+ return NErr_OutOfMemory;
+ tag->Parse(bytes, bytes_len);
+ *t = (nsid3v2_tag_t)tag;
+ return NErr_Success;
+ }
+ case 4:
+ {
+ ID3v2_4::Tag *tag = new (std::nothrow) ID3v2_4::Tag(*header);
+ if (!tag)
+ return NErr_OutOfMemory;
+ tag->Parse(bytes, bytes_len);
+ *t = (nsid3v2_tag_t)tag;
+ return NErr_Success;
+ }
+ default:
+ return NErr_NotImplemented;
+ }
+}
+
+int NSID3v2_Tag_Destroy(nsid3v2_tag_t t)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ delete tag;
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_RemoveFrame(nsid3v2_tag_t t, nsid3v2_frame_t f)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ tag->RemoveFrame(frame);
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_CreateFrame(nsid3v2_tag_t t, int frame_enum, int flags, nsid3v2_frame_t *f)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ *f = (nsid3v2_frame_t)tag->NewFrame(frame_enum, flags);
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_AddFrame(nsid3v2_tag_t t, nsid3v2_frame_t f)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ tag->AddFrame(frame);
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_GetFrame(const nsid3v2_tag_t t, int frame_enum, nsid3v2_frame_t *frame)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ ID3v2::Frame *found_frame = tag->FindFirstFrame(frame_enum);
+ if (found_frame)
+ {
+ *frame = (nsid3v2_frame_t)found_frame;
+ return NErr_Success;
+ }
+ else
+ return NErr_Empty;
+}
+
+int NSID3v2_Tag_GetNextFrame(const nsid3v2_tag_t t, const nsid3v2_frame_t f, nsid3v2_frame_t *frame)
+{
+ ID3v2::Tag *tag = (ID3v2::Tag *)t;
+ ID3v2::Frame *start_frame = (ID3v2::Frame *)f;
+
+ ID3v2::Frame *found_frame = tag->FindNextFrame(start_frame);
+ if (found_frame)
+ {
+ *frame = (nsid3v2_frame_t)found_frame;
+ return NErr_Success;
+ }
+ else
+ return NErr_EndOfEnumeration;
+}
+
+int NSID3v2_Frame_Binary_Get(nsid3v2_frame_t f, const void **binary, size_t *length)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (!frame)
+ return NErr_BadParameter;
+
+ return frame->GetData(binary, length);
+}
+
+int NSID3v2_Tag_EnumerateFrame(const nsid3v2_tag_t t, nsid3v2_frame_t p, nsid3v2_frame_t *f)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ const ID3v2::Frame *frame = tag->EnumerateFrame((const ID3v2::Frame *)p);
+ *f = (nsid3v2_frame_t)frame;
+ if (frame)
+ {
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+}
+
+int NSID3v2_Tag_GetInformation(nsid3v2_tag_t t, uint8_t *version, uint8_t *revision, int *flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ *version = tag->GetVersion();
+ *revision = tag->GetRevision();
+
+ int local_flags=0;
+ if (tag->HasExtendedHeader())
+ local_flags |= NSID3V2_TAGFLAG_EXTENDED_HEADER;
+
+ if (tag->Unsynchronised())
+ local_flags |= NSID3V2_TAGFLAG_UNSYNCHRONIZED;
+ if (tag->HasFooter())
+ local_flags |= NSID3V2_TAGFLAG_HASFOOTER;
+
+ *flags = local_flags;
+
+ return NErr_Success;
+}
+
+int NSID3v2_Tag_SerializedSize(nsid3v2_tag_t t, uint32_t *length, uint32_t padding_size, int flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ return tag->SerializedSize(length, padding_size, flags);
+}
+
+int NSID3v2_Tag_Serialize(nsid3v2_tag_t t, void *data, uint32_t len, int flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ if (!tag)
+ return NErr_BadParameter;
+
+ return tag->Serialize(data, len, flags);
+}
+/*
+================= Frame =================
+= =
+=========================================
+*/
+
+int NSID3v2_Frame_GetIdentifier(nsid3v2_frame_t f, const char **identifier)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (!frame)
+ return NErr_BadParameter;
+
+ *identifier = (const char *)frame->GetIdentifier();
+ return NErr_Success;
+}
+
+
+int NSID3v2_Frame_GetInformation(nsid3v2_frame_t f, int *type, int *flags)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ if (!frame)
+ return NErr_BadParameter;
+
+ const char *identifier=0;
+ int ret = NSID3v2_Frame_GetIdentifier(f, &identifier);
+ if (ret != NErr_Success)
+ return ret;
+
+ /* TODO: make a method to get the version from the frame */
+
+ /* ok this is a bit of hack job */
+ if (!memcmp(identifier, "TXX", 4) || !memcmp(identifier, "TXXX", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_USERTEXT;
+ }
+ else if (!memcmp(identifier, "COM", 4) || !memcmp(identifier, "COMM", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_COMMENTS;
+ }
+ else if (identifier[0] == 'T') /* check for text */
+ {
+ *type = NSID3V2_FRAMETYPE_TEXT;
+ }
+ else if (!memcmp(identifier, "WXX", 4) || !memcmp(identifier, "WXXX", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_USERURL;
+ }
+ else if (identifier[0] == 'W') /* check for URL */
+ {
+ *type = NSID3V2_FRAMETYPE_URL;
+ }
+ else if (!memcmp(identifier, "PRIV", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_PRIVATE;
+ }
+ else if (!memcmp(identifier, "GEO", 4) || !memcmp(identifier, "GEOB", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_OBJECT;
+ }
+ else if (!memcmp(identifier, "POP", 4) || !memcmp(identifier, "POPM", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_POPULARITY;
+ }
+ else if (!memcmp(identifier, "PIC", 4) || !memcmp(identifier, "APIC", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_PICTURE;
+ }
+ else if (!memcmp(identifier, "UFI", 4) || !memcmp(identifier, "UFID", 4))
+ {
+ *type = NSID3V2_FRAMETYPE_ID;
+ }
+ else
+ {
+ *type = NSID3V2_FRAMETYPE_UNKNOWN;
+ }
+
+ if (flags)
+ {
+ int local_flags=0;
+ if (frame->TagAlterPreservation())
+ local_flags |= NSID3V2_FRAMEFLAG_TAG_ALTER_PRESERVATION;
+ if (frame->FileAlterPreservation())
+ local_flags |= NSID3V2_FRAMEFLAG_FILE_ALTER_PRESERVATION;
+ if (frame->Encrypted())
+ local_flags |= NSID3V2_FRAMEFLAG_ENCRYPTED;
+ if (frame->Compressed())
+ local_flags |= NSID3V2_FRAMEFLAG_COMPRESSED;
+ if (frame->Grouped())
+ local_flags |= NSID3V2_FRAMEFLAG_GROUPED;
+ if (frame->ReadOnly())
+ local_flags |= NSID3V2_FRAMEFLAG_READONLY;
+ if (frame->FrameUnsynchronised())
+ local_flags |= NSID3V2_FRAMEFLAG_UNSYNCHRONIZED;
+ if (frame->DataLengthIndicated())
+ local_flags |= NSID3V2_FRAMEFLAG_DATALENGTHINDICATED;
+ *flags = local_flags;
+ }
+
+ return NErr_Success;
+}
+
diff --git a/Src/replicant/nsid3v2/precomp.h b/Src/replicant/nsid3v2/precomp.h
new file mode 100644
index 000000000..48c668669
--- /dev/null
+++ b/Src/replicant/nsid3v2/precomp.h
@@ -0,0 +1,32 @@
+//
+// precomp.h
+// nsid3v2
+//
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+
+#include "nu/utf.h"
+#include "nx/nxstring.h"
+
+
+#ifdef __cplusplus
+
+#include <new>
+#include "nu/AutoWide.h"
+#include "nu/PtrDeque.h"
+
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+
+#endif
diff --git a/Src/replicant/nsid3v2/tag.cpp b/Src/replicant/nsid3v2/tag.cpp
new file mode 100644
index 000000000..effb99fd4
--- /dev/null
+++ b/Src/replicant/nsid3v2/tag.cpp
@@ -0,0 +1,644 @@
+#include "tag.h"
+#include "frameheader.h"
+#include "frames.h"
+#include "foundation/error.h"
+#include <string.h>
+#include <new>
+#include "nsid3v2.h" // for serialize flags
+
+/* === ID3v2 common === */
+static bool IdentifierMatch(const int8_t *id1, const int8_t *id2)
+{
+ return !memcmp(id1, id2, 4);
+}
+
+
+ID3v2::Tag::Tag(const ID3v2::Header &_header) : Header(_header)
+{
+}
+
+void ID3v2::Tag::RemoveFrame(ID3v2::Frame *frame)
+{
+ frames.erase(frame);
+ delete frame;
+}
+
+ID3v2::Frame *ID3v2::Tag::FindFirstFrame(const int8_t *id) const
+{
+ if (!id)
+ return 0;
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ if (IdentifierMatch(itr->GetIdentifier(), id))
+ return *itr;
+ }
+ return 0;
+}
+
+ID3v2::Frame *ID3v2::Tag::FindNextFrame(const Frame *frame) const
+{
+ FrameList::const_iterator itr=frame;
+ for (itr++;itr != frames.end();itr++)
+ {
+ if (IdentifierMatch(itr->GetIdentifier(), frame->GetIdentifier()))
+ return *itr;
+ }
+ return 0;
+}
+
+void ID3v2::Tag::RemoveFrames(const int8_t *id)
+{
+ // TODO: not exactly the fastest way
+ Frame *frame;
+ while (frame = FindFirstFrame(id))
+ frames.erase(frame);
+}
+void ID3v2::Tag::AddFrame(ID3v2::Frame *frame)
+{
+ frames.push_back(frame);
+}
+
+ID3v2::Frame *ID3v2::Tag::EnumerateFrame(const ID3v2::Frame *position) const
+{
+ if (!position)
+ return frames.front();
+ else
+ return (ID3v2::Frame *)position->next;
+
+}
+/* === ID3v2.2 === */
+
+ID3v2_2::Tag::Tag(const ID3v2::Header &_header) : ID3v2::Tag(_header), extendedHeader(_header)
+{
+}
+
+ID3v2_2::Tag::~Tag()
+{
+ frames.deleteAll();
+}
+
+static inline void Advance(const void *&data, size_t &len, size_t amount)
+{
+ data = (const uint8_t *)data + amount;
+ len -= amount;
+}
+
+int ID3v2_2::Tag::Parse(const void *data, size_t len)
+{
+ /* Is there an extended header? */
+ if (Header::HasExtendedHeader())
+ {
+ size_t read=0;
+ if (extendedHeader.Parse(data, len, &read) != 0)
+ {
+ return 1;
+ }
+ Advance(data, len, read);
+ }
+
+ /* Read each frame */
+ while (len >= FrameHeader::SIZE)
+ {
+ /* if next byte is zero, we've hit the padding area, GTFO */
+ if (*(uint8_t *)data == 0x0)
+ break;
+
+ /* Read frame header first */
+ FrameHeader frame_header(*this, data);
+ Advance(data, len, FrameHeader::SIZE);
+
+ if (!frame_header.IsValid())
+ return 1;
+
+ /* read frame data */
+ Frame *new_frame = new (std::nothrow) Frame(frame_header);
+ if (!new_frame)
+ return NErr_OutOfMemory;
+
+ size_t read=0;
+ if (new_frame->Parse(data, len, &read) == 0)
+ {
+ Advance(data, len, read);
+ frames.push_back(new_frame);
+ }
+ else
+ {
+ delete new_frame;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int ID3v2_2::Tag::SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const
+{
+ size_t current_length=0;
+
+ Header new_header(*this);
+ /* TODO: going to clear this, for now */
+ new_header.ClearExtendedHeader();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ current_length += Header::SIZE;
+
+ if (new_header.HasExtendedHeader())
+ {
+ // TODO: deal with extended header
+ }
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ uint32_t written;
+ const ID3v2_2::Frame *frame = (const ID3v2_2::Frame *)*itr;
+ int ret = frame->SerializedSize(&written, new_header, flags);
+ if (ret != NErr_Success)
+ return ret;
+ current_length += written;
+ }
+
+ switch(flags & SerializedSize_PaddingMask)
+ {
+ case SerializedSize_Padding:
+ current_length += padding_size;
+ break;
+ case SerializedSize_AbsoluteSize:
+ if (current_length < padding_size)
+ current_length = padding_size;
+ break;
+ case SerializedSize_BlockSize:
+ {
+ uint32_t additional = current_length % padding_size;
+ current_length += additional;
+ }
+ break;
+ }
+
+ *length = current_length;
+ return NErr_Success;
+}
+
+int ID3v2_2::Tag::Serialize(void *data, uint32_t len, int flags) const
+{
+ uint8_t *data_itr = (uint8_t *)data;
+ uint32_t current_length=0;
+
+ // write header. note the passed-in length is guaranteed to be correct, as it was generated by SerializedSize
+ Header new_header(this, len-10);
+ /* TODO: going to clear this, for now */
+ new_header.ClearExtendedHeader();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ new_header.Serialize(data);
+
+ current_length += Header::SIZE;
+ data_itr += Header::SIZE;
+
+ if (new_header.HasExtendedHeader())
+ {
+ // TODO: write extended header
+ }
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ uint32_t written;
+ const ID3v2_2::Frame *frame = (const ID3v2_2::Frame *)*itr;
+ int ret = frame->Serialize((void *)data_itr, &written, new_header, flags);
+ if (ret != NErr_Success)
+ return ret;
+ current_length += written;
+ data_itr += written;
+ }
+
+ // write padding
+ memset(data_itr, 0, len-current_length);
+
+ return NErr_Success;
+}
+
+static bool IdentifierMatch3(const int8_t *id1, const int8_t *id2)
+{
+ return !memcmp(id1, id2, 3);
+}
+
+ID3v2_2::Frame *ID3v2_2::Tag::FindFirstFrame(int frame_id) const
+{
+ if (!ValidFrameID(frame_id))
+ return 0;
+ return (ID3v2_2::Frame *)ID3v2::Tag::FindFirstFrame(frame_ids[frame_id].v2);
+};
+
+
+void ID3v2_2::Tag::RemoveFrames(int frame_id)
+{
+ // TODO: not exactly the fastest way
+ Frame *frame;
+ while (frame = FindFirstFrame(frame_id))
+ frames.erase(frame);
+}
+
+ID3v2_2::Frame *ID3v2_2::Tag::NewFrame(int frame_id, int flags) const
+{
+ if (!ValidFrameID(frame_id) || !frame_ids[frame_id].v2)
+ return 0;
+ return new (std::nothrow) ID3v2_2::Frame(*this, frame_ids[frame_id].v2, flags);
+}
+
+/* === ID3v2.3 === */
+
+ID3v2_3::Tag::Tag(const ID3v2::Header &_header) : ID3v2::Tag(_header), extendedHeader(_header)
+{
+}
+
+ID3v2_3::Tag::~Tag()
+{
+ frames.deleteAll();
+}
+
+int ID3v2_3::Tag::Parse(const void *data, size_t len)
+{
+ /* Is there an extended header? */
+ if (Header::HasExtendedHeader())
+ {
+ size_t read=0;
+ if (extendedHeader.Parse(data, len, &read) != 0)
+ {
+ return 1;
+ }
+ Advance(data, len, read);
+ }
+
+ /* Read each frame */
+ while (len >= FrameHeader::SIZE)
+ {
+ /* if next byte is zero, we've hit the padding area, GTFO */
+ if (*(uint8_t *)data == 0x0)
+ break;
+
+ /* Read frame header first */
+ FrameHeader frame_header(*this, data);
+ Advance(data, len, FrameHeader::SIZE);
+
+ if (!frame_header.IsValid())
+ return 1;
+
+ /* read frame data */
+ Frame *new_frame = new (std::nothrow) Frame(frame_header);
+ if (!new_frame)
+ return NErr_OutOfMemory;
+
+ size_t read=0;
+ if (new_frame->Parse(data, len, &read) == 0)
+ {
+ Advance(data, len, read);
+ frames.push_back(new_frame);
+ }
+ else
+ {
+ delete new_frame;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int ID3v2_3::Tag::SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const
+{
+ size_t current_length=0;
+
+ Header new_header(*this);
+ /* TODO: going to clear this, for now */
+ new_header.ClearExtendedHeader();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ current_length += Header::SIZE;
+
+ if (new_header.HasExtendedHeader())
+ {
+ // TODO: deal with extended header
+ }
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ uint32_t written;
+ const ID3v2_3::Frame *frame = (const ID3v2_3::Frame *)*itr;
+ int ret = frame->SerializedSize(&written, new_header, flags);
+ if (ret != NErr_Success)
+ return ret;
+ current_length += written;
+ }
+
+ switch(flags & SerializedSize_PaddingMask)
+ {
+ case SerializedSize_Padding:
+ current_length += padding_size;
+ break;
+ case SerializedSize_AbsoluteSize:
+ if (current_length < padding_size)
+ current_length = padding_size;
+ break;
+ case SerializedSize_BlockSize:
+ {
+ uint32_t additional = padding_size - (current_length % padding_size);
+ if (additional == padding_size)
+ additional=0;
+ current_length += additional;
+ }
+ break;
+ }
+
+ *length = current_length;
+ return NErr_Success;
+}
+
+int ID3v2_3::Tag::Serialize(void *data, uint32_t len, int flags) const
+{
+ uint8_t *data_itr = (uint8_t *)data;
+ uint32_t current_length=0;
+
+ // write header. note the passed-in length is guaranteed to be correct, as it was generated by SerializedSize
+ Header new_header(this, len-10);
+ /* TODO: going to clear this, for now */
+ new_header.ClearExtendedHeader();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ new_header.Serialize(data);
+
+ current_length += Header::SIZE;
+ data_itr += Header::SIZE;
+
+ if (new_header.HasExtendedHeader())
+ {
+ // TODO: write extended header
+ }
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ uint32_t written;
+ const ID3v2_3::Frame *frame = (const ID3v2_3::Frame *)*itr;
+ int ret = frame->Serialize((void *)data_itr, &written, new_header, flags);
+ if (ret != NErr_Success)
+ return ret;
+ current_length += written;
+ data_itr += written;
+ }
+
+ // write padding
+ memset(data_itr, 0, len-current_length);
+
+ return NErr_Success;
+}
+
+ID3v2_3::Frame *ID3v2_3::Tag::FindFirstFrame(int frame_id) const
+{
+ if (!ValidFrameID(frame_id))
+ return 0;
+ return (ID3v2_3::Frame *)ID3v2::Tag::FindFirstFrame(frame_ids[frame_id].v3);
+};
+
+void ID3v2_3::Tag::RemoveFrames(int frame_id)
+{
+ // TODO: not exactly the fastest way
+ Frame *frame;
+ while (frame = FindFirstFrame(frame_id))
+ frames.erase(frame);
+}
+
+ID3v2_3::Frame *ID3v2_3::Tag::NewFrame(int frame_id, int flags) const
+{
+ if (!ValidFrameID(frame_id) || !frame_ids[frame_id].v3)
+ return 0;
+ return new (std::nothrow) ID3v2_3::Frame(*this, frame_ids[frame_id].v3, flags);
+}
+
+/* === ID3v2.4 === */
+ID3v2_4::Tag::Tag(const ID3v2::Header &_header) : ID3v2::Tag(_header), extendedHeader(_header)
+{
+}
+
+ID3v2_4::Tag::~Tag()
+{
+ frames.deleteAll();
+}
+
+int ID3v2_4::Tag::Parse(const void *data, size_t len)
+{
+ /* Is there an extended header? */
+ if (Header::HasExtendedHeader())
+ {
+ size_t read=0;
+ if (extendedHeader.Parse(data, len, &read) != 0)
+ {
+ return 1;
+ }
+ Advance(data, len, read);
+ }
+
+ /* Read each frame */
+ while (len >= FrameHeader::SIZE)
+ {
+ /* if next byte is zero, we've hit the padding area, GTFO */
+ if (*(uint8_t *)data == 0x0)
+ break;
+
+ /* Read frame header first */
+ FrameHeader frame_header(*this, data);
+ Advance(data, len, FrameHeader::SIZE);
+
+ if (!frame_header.IsValid())
+ return 1;
+
+ /* read frame data */
+ Frame *new_frame = new (std::nothrow) Frame(frame_header);
+ if (!new_frame)
+ return NErr_OutOfMemory;
+
+ size_t read=0;
+ if (new_frame->Parse(data, len, &read) == 0)
+ {
+ Advance(data, len, read);
+ frames.push_back(new_frame);
+ }
+ else
+ {
+ delete new_frame;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int ID3v2_4::Tag::SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const
+{
+ size_t current_length=0;
+
+ Header new_header(*this);
+ /* TODO: going to clear this, for now */
+ new_header.ClearExtendedHeader();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ current_length += Header::SIZE;
+
+ if (new_header.HasExtendedHeader())
+ {
+ // TODO: deal with extended header
+ }
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ uint32_t written;
+ const ID3v2_4::Frame *frame = (const ID3v2_4::Frame *)*itr;
+ int ret = frame->SerializedSize(&written, new_header, flags);
+ if (ret != NErr_Success)
+ return ret;
+ current_length += written;
+ }
+
+ switch(flags & SerializedSize_PaddingMask)
+ {
+ case 0:
+ /* we can only write a footer if there is no padding */
+ if (new_header.FooterValid() || new_header.HasFooter())
+ {
+ current_length += Header::SIZE;
+ }
+ break;
+ case SerializedSize_Padding:
+ current_length += padding_size;
+ break;
+ case SerializedSize_AbsoluteSize:
+ if (current_length < padding_size)
+ current_length = padding_size;
+ break;
+ case SerializedSize_BlockSize:
+ {
+ uint32_t additional = current_length % padding_size;
+ current_length += additional;
+ }
+ break;
+ }
+
+ *length = current_length;
+ return NErr_Success;
+}
+
+int ID3v2_4::Tag::Serialize(void *data, uint32_t len, int flags) const
+{
+ uint8_t *data_itr = (uint8_t *)data;
+ uint32_t current_length=0;
+
+ // write header. note the passed-in length is guaranteed to be correct, as it was generated by SerializedSize
+ bool write_footer=false;
+ if ((flags & SerializedSize_PaddingMask) == 0 && (FooterValid() || HasFooter()))
+ write_footer=true;
+
+ Header new_header(this, write_footer?(len-20):(len-10));
+ new_header.SetFooter(write_footer);
+
+ /* TODO: going to clear this, for now */
+ new_header.ClearExtendedHeader();
+ switch(flags & Serialize_UnsynchronizeMask)
+ {
+ case Serialize_Unsynchronize:
+ // TODO:
+ break;
+ case Serialize_NoUnsynchronize:
+ new_header.ClearUnsynchronized();
+ break;
+ }
+
+ new_header.SerializeAsHeader(data);
+
+ current_length += Header::SIZE;
+ data_itr += Header::SIZE;
+
+ if (new_header.HasExtendedHeader())
+ {
+ // TODO: write extended header
+ }
+
+ for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
+ {
+ uint32_t written;
+ const ID3v2_4::Frame *frame = (const ID3v2_4::Frame *)*itr;
+ int ret = frame->Serialize((void *)data_itr, &written, new_header, flags);
+ if (ret != NErr_Success)
+ return ret;
+ current_length += written;
+ data_itr += written;
+ }
+
+ if (write_footer)
+ {
+ new_header.SerializeAsFooter(data_itr);
+ current_length += Header::SIZE;
+ data_itr += Header::SIZE;
+ }
+
+ // write padding
+ memset(data_itr, 0, len-current_length);
+
+ return NErr_Success;
+}
+
+ID3v2_4::Frame *ID3v2_4::Tag::FindFirstFrame(int frame_id) const
+{
+ if (!ValidFrameID(frame_id))
+ return 0;
+ return (ID3v2_4::Frame *)ID3v2::Tag::FindFirstFrame(frame_ids[frame_id].v4);
+};
+
+void ID3v2_4::Tag::RemoveFrames(int frame_id)
+{
+ // TODO: not exactly the fastest way
+ Frame *frame;
+ while (frame = FindFirstFrame(frame_id))
+ frames.erase(frame);
+}
+
+ID3v2_4::Frame *ID3v2_4::Tag::NewFrame(int frame_id, int flags) const
+{
+ if (!ValidFrameID(frame_id) || !frame_ids[frame_id].v4)
+ return 0;
+ return new (std::nothrow) ID3v2_4::Frame(*this, frame_ids[frame_id].v4, flags);
+}
diff --git a/Src/replicant/nsid3v2/tag.h b/Src/replicant/nsid3v2/tag.h
new file mode 100644
index 000000000..0f661a4ac
--- /dev/null
+++ b/Src/replicant/nsid3v2/tag.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include "header.h"
+#include "nu/PtrDeque.h"
+#include "frame.h"
+#include "extendedheader.h"
+
+/* benski> random thoughts
+
+Frames are stored as raw bytes. Users of this library will have to parse/encode
+on their own. But we'll supply a helper library for that.
+
+new frames must be created from the tag object (not standalone "new ID3v2::Frame")
+so that revision & version information (and any relevant tag-wide flags) can be inherited
+*/
+namespace ID3v2
+{
+ class Tag : public ID3v2::Header
+ {
+ public:
+ Tag(const ID3v2::Header &header);
+ virtual ~Tag() {}
+ /* finds the first frame with the desired identifier. */
+ ID3v2::Frame *FindFirstFrame(const int8_t *id) const;
+ /* finds the next frame with the same identifier as the passed in frame
+ "frame" parameter MUST be a frame returned from the same Tag object! */
+ ID3v2::Frame *FindNextFrame(const ID3v2::Frame *frame) const;
+ void RemoveFrame(ID3v2::Frame *frame);
+ void RemoveFrames(const int8_t *id);
+ void AddFrame(ID3v2::Frame *frame);
+ virtual int Parse(const void *data, size_t len)=0;
+ virtual int SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const=0;
+ // tag will be padded up to length. use SerializedSize() to retrieve the length to use!
+ virtual int Serialize(void *data, uint32_t len, int flags) const=0;
+ virtual ID3v2::Frame *FindFirstFrame(int frame_id) const = 0;
+ virtual void RemoveFrames(int frame_id)=0;
+ virtual ID3v2::Frame *NewFrame(int frame_id, int flags) const=0;
+ ID3v2::Frame *EnumerateFrame(const ID3v2::Frame *position) const;
+ protected:
+ typedef nu::PtrDeque<ID3v2::Frame> FrameList;
+ nu::PtrDeque<ID3v2::Frame> frames;
+ };
+}
+namespace ID3v2_2
+{
+ class Tag : public ID3v2::Tag
+ {
+ public:
+ Tag(const ID3v2::Header &header);
+ ~Tag();
+ int Parse(const void *data, size_t len);
+ int SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const;
+ int Serialize(void *data, uint32_t len, int flags) const;
+ /* finds the first frame with the desired identifier. */
+ ID3v2_2::Frame *FindFirstFrame(int frame_id) const;
+ /* finds the next frame with the same identifier as the passed in frame
+ "frame" parameter MUST be a frame returned from the same Tag object! */
+ void RemoveFrames(int frame_id);
+ ID3v2_2::Frame *NewFrame(int frame_id, int flags) const;
+ private:
+ ID3v2_3::ExtendedHeader extendedHeader;
+ };
+}
+
+namespace ID3v2_3
+{
+ class Tag : public ID3v2::Tag
+ {
+ public:
+ Tag(const ID3v2::Header &header);
+ ~Tag();
+ int Parse(const void *data, size_t len);
+ int SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const;
+ int Serialize(void *data, uint32_t len, int flags) const;
+ /* finds the first frame with the desired identifier. */
+ ID3v2_3::Frame *FindFirstFrame(int frame_id) const;
+
+ void RemoveFrames(int frame_id);
+ ID3v2_3::Frame *NewFrame(int frame_id, int flags) const;
+ private:
+ ID3v2_3::ExtendedHeader extendedHeader;
+ };
+}
+
+namespace ID3v2_4
+{
+ class Tag : public ID3v2::Tag
+ {
+ public:
+ Tag(const ID3v2::Header &header);
+ ~Tag();
+ int Parse(const void *data, size_t len);
+ int SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const;
+ int Serialize(void *data, uint32_t len, int flags) const;
+ /* finds the first frame with the desired identifier. */
+ ID3v2_4::Frame *FindFirstFrame(int frame_id) const;
+ /* finds the next frame with the same identifier as the passed in frame
+ "frame" parameter MUST be a frame returned from the same Tag object! */
+ ID3v2::Frame *FindNextFrame(const ID3v2::Frame *frame) const { return FindNextFrame((const Frame *)frame); }
+
+ void RemoveFrames(int frame_id);
+ ID3v2_4::Frame *NewFrame(int frame_id, int flags) const;
+ private:
+ ID3v2_4::ExtendedHeader extendedHeader;
+ };
+}
diff --git a/Src/replicant/nsid3v2/util.cpp b/Src/replicant/nsid3v2/util.cpp
new file mode 100644
index 000000000..21e09d377
--- /dev/null
+++ b/Src/replicant/nsid3v2/util.cpp
@@ -0,0 +1,161 @@
+#include "util.h"
+
+uint32_t ID3v2::Util::Int28To32(uint32_t val)
+{
+ // TODO: big endian safe?
+ uint32_t ret;
+
+ ret = (val & 0x7FU);
+ val >>= 1;
+ ret |= (val & 0x3F80U);
+ val >>= 1;
+ ret |= (val & 0x1FC000U);
+ val >>= 1;
+ ret |= (val & 0xFE00000U);
+
+ return ret;
+ /*
+ uint8_t *bytes = (uint8_t *)&ret;
+ const uint8_t *value = (const uint8_t *)&val;
+
+ ret = (value[0] << 21) + (value[1] << 14) + (value[2] << 7) + (value[3]);
+
+ // for (size_t i=0;i<sizeof(uint32_t);i++ )
+ // value[sizeof(uint32_t)-1-i]=(uint8_t)(val>>(i*8)) & 0xFF;
+
+ return ret;
+ */
+}
+
+uint32_t ID3v2::Util::Int32To28(uint32_t val)
+{
+ // TODO: big endian safe?
+ uint32_t ret;
+ ret = (val & 0x7FU);
+ ret |= (val & 0x3F80U) << 1;
+ ret |= (val & 0x1FC000U) << 2;
+ ret |= (val & 0xFE00000U) << 3;
+
+ return ret;
+}
+
+size_t ID3v2::Util::UnsynchroniseTo(void *_output, const void *_input, size_t bytes)
+{
+ uint8_t *output = (uint8_t *)_output;
+ const uint8_t *input = (const uint8_t *)_input;
+ size_t bytes_read = 0;
+ while (bytes)
+ {
+ if (input[0] == 0xFF && input[1] == 0)
+ {
+ *output++ = 0xFF;
+ input+=2;
+ bytes_read+=2;
+ bytes--;
+ }
+ else
+ {
+ *output++=*input++;
+ bytes_read++;
+ bytes--;
+ }
+ }
+ return bytes_read;
+}
+
+size_t ID3v2::Util::UnsynchronisedInputSize(const void *data, size_t output_bytes)
+{
+ const uint8_t *input = (const uint8_t *)data;
+ size_t bytes_read = 0;
+ while (output_bytes)
+ {
+ if (input[0] == 0xFF && input[1] == 0)
+ {
+ input+=2;
+ bytes_read+=2;
+ output_bytes--;
+ }
+ else
+ {
+ input++;
+ bytes_read++;
+ output_bytes--;
+ }
+ }
+ return bytes_read;
+}
+
+size_t ID3v2::Util::UnsynchronisedOutputSize(const void *data, size_t input_bytes)
+{
+ const uint8_t *input = (const uint8_t *)data;
+ size_t bytes_written = 0;
+ while (input_bytes)
+ {
+ if (input[0] == 0xFF && input_bytes > 1 && input[1] == 0)
+ {
+ input+=2;
+ bytes_written++;
+ input_bytes-=2;
+ }
+ else
+ {
+ input++;
+ bytes_written++;
+ input_bytes--;
+ }
+ }
+ return bytes_written;
+}
+
+// returns output bytes used
+size_t ID3v2::Util::SynchroniseTo(void *_output, const void *data, size_t bytes)
+{
+ uint8_t *output = (uint8_t *)_output;
+ const uint8_t *input = (const uint8_t *)data;
+ size_t bytes_needed = 0;
+ while (bytes)
+ {
+ *output++=*input;
+ bytes_needed++;
+ if (*input++ == 0xFF)
+ {
+ if (bytes == 1)
+ {
+ // if this is the last byte, we need to make room for an extra 0
+ *output = 0;
+ return bytes_needed + 1;
+ }
+ else if ((*input & 0xE0) == 0xE0 || *input == 0)
+ {
+ *output++ = 0;
+ bytes_needed++;
+ }
+ }
+ bytes--;
+ }
+ return bytes_needed;
+}
+
+size_t ID3v2::Util::SynchronisedSize(const void *data, size_t bytes)
+{
+ const uint8_t *input = (const uint8_t *)data;
+ size_t bytes_needed = 0;
+ while (bytes)
+ {
+ bytes_needed++;
+ if (*input++ == 0xFF)
+ {
+ if (bytes == 1)
+ {
+ // if this is the last byte, we need to make room for an extra 0
+ return bytes_needed + 1;
+ }
+ else if ((*input & 0xE0) == 0xE0 || *input == 0)
+ {
+ bytes_needed++;
+ }
+ }
+ bytes--;
+ }
+ return bytes_needed;
+}
diff --git a/Src/replicant/nsid3v2/util.h b/Src/replicant/nsid3v2/util.h
new file mode 100644
index 000000000..11ca11809
--- /dev/null
+++ b/Src/replicant/nsid3v2/util.h
@@ -0,0 +1,28 @@
+#pragma once
+#include "foundation/types.h"
+
+namespace ID3v2
+{
+ namespace Util
+ {
+ /* pass a value you read as if it was a 32bit integer */
+ uint32_t Int28To32(uint32_t val);
+
+ uint32_t Int32To28(uint32_t val);
+
+ // returns input bytes used
+ size_t UnsynchroniseTo(void *output, const void *input, size_t output_bytes);
+
+ // returns number of real bytes required to read 'bytes' data
+ size_t UnsynchronisedInputSize(const void *input, size_t output_bytes);
+
+
+ size_t UnsynchronisedOutputSize(const void *input, size_t input_bytes);
+
+ // returns output bytes used
+ size_t SynchroniseTo(void *output, const void *input, size_t bytes);
+
+ // returns number of bytes required to store synchronized version of 'data' (bytes long)
+ size_t SynchronisedSize(const void *data, size_t bytes);
+ }
+}
diff --git a/Src/replicant/nsid3v2/values.cpp b/Src/replicant/nsid3v2/values.cpp
new file mode 100644
index 000000000..843cec3fc
--- /dev/null
+++ b/Src/replicant/nsid3v2/values.cpp
@@ -0,0 +1,49 @@
+#include "values.h"
+
+uint8_t ID3v2::Values::ValidHeaderMask(uint8_t version, uint8_t revision)
+{
+ switch(version)
+ {
+ case 2:
+ if (revision == 1)
+ return 0xE0;
+ else
+ return 0xC0;
+ case 4:
+ return 0xF0; /* 11110000 */
+ case 3:
+ return 0xE0; /* 11100000 */
+ default:
+ return 0;
+ }
+}
+
+bool ID3v2::Values::KnownVersion(uint8_t version, uint8_t revision)
+{
+ if (version > Values::MAX_VERSION)
+ return false;
+
+ if (version < Values::MIN_VERSION)
+ return false;
+
+ return true;
+}
+
+uint8_t ID3v2::Values::ExtendedHeaderFlag(uint8_t version, uint8_t revision)
+{
+ switch(version)
+ {
+ case 2:
+ if (revision == 1)
+ return (1 << 6);
+ else
+ return 0;
+
+ case 3:
+ case 4:
+ return (1 << 6);
+
+ default:
+ return 0;
+ }
+}
diff --git a/Src/replicant/nsid3v2/values.h b/Src/replicant/nsid3v2/values.h
new file mode 100644
index 000000000..ee6f2e50e
--- /dev/null
+++ b/Src/replicant/nsid3v2/values.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "foundation/types.h"
+
+/* benski>
+This is where we encapsulate all data.
+Everything is implemented by a function that accepts a version and revision.
+*/
+namespace ID3v2
+{
+ namespace Values
+ {
+ enum
+ {
+ MIN_VERSION = 2,
+ MAX_VERSION = 4,
+ };
+
+ bool KnownVersion(uint8_t version, uint8_t revision);
+ uint8_t ValidHeaderMask(uint8_t version, uint8_t revision);
+ uint8_t ExtendedHeaderFlag(uint8_t version, uint8_t revision);
+ }
+};
diff --git a/Src/replicant/nsid3v2/windows/frame_apic.cpp b/Src/replicant/nsid3v2/windows/frame_apic.cpp
new file mode 100644
index 000000000..0e937deee
--- /dev/null
+++ b/Src/replicant/nsid3v2/windows/frame_apic.cpp
@@ -0,0 +1,170 @@
+#include "nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include <api/memmgr/api_memmgr.h>
+#include <strsafe.h>
+
+struct ParsedPicture
+{
+ uint8_t encoding; // 0 - iso-8859-1, 1 - UTF16LE, 2 - UTF16BE, 3 - UTF8
+ const char *mime_type;
+ size_t mime_cch;
+ uint8_t picture_type;
+ union
+ {
+ const char *as8;
+ const wchar_t *as16;
+ } description_data;
+ size_t description_cch;
+ const void *picture_data;
+ size_t picture_bytes;
+};
+
+static int ParsePicture(const void *data, size_t data_len, ParsedPicture &parsed)
+{
+ const uint8_t *data8 = (const uint8_t *)data;
+ parsed.encoding = data8[0];
+ parsed.mime_type = (const char *)&data8[1];
+ data_len--;
+ ParseDescription(parsed.mime_type, data_len, parsed.mime_cch);
+ parsed.picture_type = data8[2+parsed.mime_cch];
+ data_len--;
+
+ switch(parsed.encoding)
+ {
+ case 0: // ISO-8859-1
+ ParseDescription(parsed.description_data.as8, parsed.description_cch, data_len);
+ parsed.picture_data = parsed.description_data.as8 + parsed.description_cch + 1;
+ parsed.picture_bytes = data_len;
+ return NErr_Success;
+ case 1: // UTF-16
+ ParseDescription(parsed.description_data.as16, parsed.description_cch, data_len, parsed.encoding);
+ parsed.picture_data = parsed.description_data.as8 + parsed.description_cch + 1;
+ parsed.picture_bytes = data_len;
+ return NErr_Success;
+
+ case 2: // UTF-16 BE
+ ParseDescription(parsed.description_data.as16, parsed.description_cch, data_len, parsed.encoding);
+ parsed.picture_data = parsed.description_data.as8 + parsed.description_cch + 1;
+ parsed.picture_bytes = data_len;
+ return NErr_Success;
+ case 3: // UTF-8
+ ParseDescription(parsed.description_data.as8, parsed.description_cch, data_len);
+ parsed.picture_data = parsed.description_data.as8 + parsed.description_cch + 1;
+ parsed.picture_bytes = data_len;
+ return NErr_Success;
+ }
+ return NErr_NotImplemented;
+}
+
+int NSID3v2_Tag_APIC_GetPicture(const nsid3v2_tag_t t, uint8_t picture_type, void *_memmgr, wchar_t **mime_type, void **picture_data, size_t *picture_bytes)
+{
+ api_memmgr *memmgr = (api_memmgr *)_memmgr;
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_PICTURE);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPicture parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePicture(data, data_len, parsed) == NErr_Success && parsed.picture_type == picture_type)
+ {
+ const char *type = strchr(parsed.mime_type, '/');
+
+ if (type && *type)
+ {
+ type++;
+ int typelen = MultiByteToWideChar(CP_ACP, 0, type, -1, 0, 0);
+ *mime_type = (wchar_t *)memmgr->sysMalloc(typelen * sizeof(wchar_t));
+ MultiByteToWideChar(CP_ACP, 0, type, -1, *mime_type, typelen);
+ }
+ else
+ *mime_type = 0; // unknown!
+
+ *picture_bytes = parsed.picture_bytes;
+ *picture_data = memmgr->sysMalloc(parsed.picture_bytes);
+ memcpy(*picture_data, parsed.picture_data, parsed.picture_bytes);
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Error;
+}
+
+
+int NSID3v2_Tag_APIC_GetFirstPicture(const nsid3v2_tag_t t, void *_memmgr, wchar_t **mime_type, void **picture_data, size_t *picture_bytes)
+{
+ api_memmgr *memmgr = (api_memmgr *)_memmgr;
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_PICTURE);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPicture parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePicture(data, data_len, parsed) == NErr_Success)
+ {
+ const char *type = strchr(parsed.mime_type, '/');
+
+ if (type && *type)
+ {
+ type++;
+ int typelen = MultiByteToWideChar(CP_ACP, 0, type, -1, 0, 0);
+ *mime_type = (wchar_t *)memmgr->sysMalloc(typelen * sizeof(wchar_t));
+ MultiByteToWideChar(CP_ACP, 0, type, -1, *mime_type, typelen);
+ }
+ else
+ *mime_type = 0; // unknown!
+
+ *picture_bytes = parsed.picture_bytes;
+ *picture_data = memmgr->sysMalloc(parsed.picture_bytes);
+ memcpy(*picture_data, parsed.picture_data, parsed.picture_bytes);
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Error;
+}
+
+int NSID3v2_Tag_APIC_GetFrame(const nsid3v2_tag_t t, uint8_t picture_type, nsid3v2_frame_t *f)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_PICTURE);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPicture parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePicture(data, data_len, parsed) == NErr_Success && parsed.picture_type == picture_type)
+ {
+ *f = (nsid3v2_frame_t)frame;
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Error;
+}
+
+int NSID3v2_Tag_APIC_GetFirstFrame(const nsid3v2_tag_t t, nsid3v2_frame_t *f)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_PICTURE);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedPicture parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParsePicture(data, data_len, parsed) == NErr_Success)
+ {
+ *f = (nsid3v2_frame_t)frame;
+ return NErr_Success;
+ }
+ frame = tag->FindNextFrame(frame);
+ }
+
+ return NErr_Error;
+} \ No newline at end of file
diff --git a/Src/replicant/nsid3v2/windows/frame_comments.cpp b/Src/replicant/nsid3v2/windows/frame_comments.cpp
new file mode 100644
index 000000000..4e1beb5fc
--- /dev/null
+++ b/Src/replicant/nsid3v2/windows/frame_comments.cpp
@@ -0,0 +1,228 @@
+#include "nsid3v2/nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include "nsid3v2/frame_utils.h"
+#include "nu/AutoWide.h"
+#include <strsafe.h>
+
+struct ParsedComments
+{
+ const char *language;
+ uint8_t description_encoding; // 0 - iso-8859-1, 1 - UTF16LE, 2 - UTF16BE, 3 - UTF8
+ union
+ {
+ const char *as8;
+ const wchar_t *as16;
+ } description_data;
+ size_t description_cch;
+ uint8_t value_encoding; // 0 - iso-8859-1, 1 - UTF16LE, 2 - UTF16BE, 3 - UTF8
+ union
+ {
+ const char *as8;
+ const wchar_t *as16;
+ } value_data;
+ size_t value_cch;
+};
+
+static int ParseComments(const void *data, size_t data_len, ParsedComments &parsed)
+{
+ int ret;
+ if (data_len < 5)
+ return NErr_Error;
+
+ const uint8_t *data8 = (const uint8_t *)data;
+ switch(data8[0])
+ {
+ case 0: // ISO-8859-1
+ parsed.description_encoding = 0;
+ parsed.language = (const char *)&data8[1];
+
+ parsed.value_encoding = 0;
+ parsed.description_data.as8 = (const char *)&data8[4];
+ data_len-=4;
+
+ ret = ParseDescription(parsed.description_data.as8, data_len, parsed.description_cch);
+ if (ret != NErr_Success)
+ return ret;
+
+ parsed.value_data.as8 = parsed.description_data.as8 + 2 + parsed.description_cch;
+ parsed.value_cch = data_len;
+
+ return NErr_Success;
+ case 1: // UTF-16
+ parsed.language = (const char *)&data8[1];
+ parsed.description_encoding=1;
+ parsed.description_data.as16 = (const wchar_t *)&data8[4];
+ data_len-=4;
+
+ ret = ParseDescription(parsed.description_data.as16, data_len, parsed.description_cch, parsed.description_encoding);
+ if (ret != NErr_Success)
+ return ret;
+
+ parsed.value_data.as16 = parsed.description_data.as16 + 2 + parsed.description_cch;
+ parsed.value_cch = data_len/2;
+
+ if (parsed.value_cch && parsed.value_data.as16[0] == 0xFFFE)
+ {
+ parsed.value_encoding=2;
+ parsed.value_data.as16++;
+ parsed.value_cch--;
+ }
+ else if (parsed.value_cch && parsed.value_data.as16[0] == 0xFEFF)
+ {
+ parsed.value_encoding=1;
+ parsed.value_data.as16++;
+ parsed.value_cch--;
+ }
+ else
+ {
+ parsed.value_encoding=1;
+ }
+
+ return NErr_Success;
+
+ case 2: // UTF-16 BE
+ parsed.language = (const char *)&data8[1];
+ parsed.description_encoding=2;
+ parsed.description_data.as16 = (const wchar_t *)&data8[4];
+ data_len-=3;
+
+ ret = ParseDescription(parsed.description_data.as16, data_len, parsed.description_cch, parsed.description_encoding);
+ if (ret != NErr_Success)
+ return ret;
+
+ parsed.value_data.as16 = parsed.description_data.as16 + 2 + parsed.description_cch;
+ parsed.value_cch = data_len/2;
+ parsed.value_encoding=2;
+
+ return NErr_Success;
+ case 3: // UTF-8
+ parsed.description_encoding = 3;
+ parsed.language = (const char *)&data8[1];
+ parsed.value_encoding = 3;
+ parsed.description_data.as8 = (const char *)&data8[4];
+ data_len-=4;
+
+ ret = ParseDescription(parsed.description_data.as8, data_len, parsed.description_cch);
+ if (ret != NErr_Success)
+ return ret;
+
+ // check for UTF-8 BOM
+ if (parsed.description_cch >= 3 && parsed.description_data.as8[0] == 0xEF && parsed.description_data.as8[1] == 0xBB && parsed.description_data.as8[2] == 0xBF)
+ {
+ parsed.description_data.as8+=3;
+ parsed.description_cch-=3;
+ }
+
+ if (!data_len)
+ return NErr_Error;
+
+ parsed.value_data.as8 = parsed.description_data.as8 + 2 + parsed.description_cch;
+ parsed.value_cch = data_len;
+
+ // check for UTF-8 BOM
+ if (parsed.value_cch >= 3 && parsed.value_data.as8[0] == 0xEF && parsed.value_data.as8[1] == 0xBB && parsed.value_data.as8[2] == 0xBF)
+ {
+ parsed.value_data.as8+=3;
+ parsed.value_cch-=3;
+ }
+
+ return NErr_Success;
+ }
+ return NErr_NotImplemented;
+}
+
+
+int NSID3v2_Tag_Comments_GetUTF16(const nsid3v2_tag_t t, const wchar_t *description, wchar_t *buf, size_t buf_cch, int text_flags)
+{
+ const ID3v2::Tag *tag = (const ID3v2::Tag *)t;
+ const ID3v2::Frame *frame = tag->FindFirstFrame(NSID3V2_FRAME_COMMENTS);
+ while (frame)
+ {
+ const void *data;
+ size_t data_len;
+ ParsedComments parsed;
+ if (frame->GetData(&data, &data_len) == NErr_Success && data_len > 0 && ParseComments(data, data_len, parsed) == NErr_Success)
+ {
+ // see if our description matches
+ switch(parsed.description_encoding)
+ {
+ case 0: // ISO-8859-1
+ {
+ UINT codepage = (text_flags & NSID3V2_TEXT_SYSTEM)?28591:CP_ACP;
+ if (CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, AutoWide(parsed.description_data.as8, codepage), -1, description, -1) != CSTR_EQUAL)
+ goto next_frame;
+ }
+ break;
+ case 1:
+ if (CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, parsed.description_data.as16, -1, description, -1) != CSTR_EQUAL)
+ goto next_frame;
+ break;
+ case 2:
+ // TODO!
+ goto next_frame;
+ break;
+ case 3:
+ if (CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, AutoWide(parsed.description_data.as8, CP_UTF8), -1, description, -1) != CSTR_EQUAL)
+ goto next_frame;
+ break;
+ }
+
+ switch(parsed.value_encoding)
+ {
+ case 0: // ISO-8859-1
+ {
+ UINT codepage = (text_flags & NSID3V2_TEXT_SYSTEM)?28591:CP_ACP;
+ int utf16_len = MultiByteToWideChar(codepage, 0, parsed.value_data.as8, parsed.value_cch, 0, 0);
+
+ if (utf16_len)
+ {
+ utf16_len = MultiByteToWideChar(codepage, 0, parsed.value_data.as8, parsed.value_cch, buf, utf16_len-1);
+ buf[utf16_len]=0;
+ }
+ else
+ {
+ buf[0]=0;
+ }
+ }
+ return NErr_Success;
+ case 1: // UTF-16
+ StringCchCopyNW(buf, buf_cch, parsed.value_data.as16, parsed.value_cch);
+ return NErr_Success;
+ case 2: // UTF-16BE
+ {
+ size_t toCopy = buf_cch-1;
+ if (parsed.value_cch < toCopy)
+ toCopy = parsed.value_cch;
+ for (size_t i=0;i<toCopy;i++)
+ {
+ buf[i] = ((parsed.value_data.as16[i] >> 8) & 0xFF) | (((parsed.value_data.as16[i]) & 0xFF) << 8);
+ }
+ buf[toCopy]=0;
+ }
+ return NErr_Success;
+ case 3: // UTF-8
+ {
+ int utf16_len = MultiByteToWideChar(CP_UTF8, 0, parsed.value_data.as8, parsed.value_cch, 0, 0);
+
+ if (utf16_len)
+ {
+ utf16_len = MultiByteToWideChar(CP_UTF8, 0, parsed.value_data.as8, parsed.value_cch, buf, utf16_len-1);
+ buf[utf16_len]=0;
+ }
+ else
+ {
+ buf[0]=0;
+ }
+ }
+ return NErr_Success;
+ }
+
+next_frame:
+ frame = tag->FindNextFrame(frame);
+ }
+ }
+
+ return NErr_Error;
+}
+
diff --git a/Src/replicant/nsid3v2/windows/nsid3v2.cpp b/Src/replicant/nsid3v2/windows/nsid3v2.cpp
new file mode 100644
index 000000000..f132be8ef
--- /dev/null
+++ b/Src/replicant/nsid3v2/windows/nsid3v2.cpp
@@ -0,0 +1,73 @@
+#include "nsid3v2/nsid3v2.h"
+#include "nsid3v2/header.h"
+#include "nsid3v2/tag.h"
+#include <new>
+#include <strsafe.h>
+
+
+/*
+================== Tag ==================
+= =
+=========================================
+*/
+#if 0 // save for reference
+int NSID3v2_Frame_Text_SetUTF16(nsid3v2_frame_t f, const wchar_t *value)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ size_t len = wcslen(value);
+ size_t bytes = len * 2 + 1; // leave 1 byte for encoding
+ if (bytes < len) // woops, integer overflow
+ return NErr_Error;
+
+ size_t datalen;
+ void *data;
+ int ret = frame->NewData(bytes, &data, &datalen);
+ if (ret == NErr_Success)
+ {
+ uint8_t *data8 = (uint8_t *)data;
+ data8[0]=1; // set encoding to UTF-16
+ memcpy(data8+1, value, len*2);
+ }
+ return ret;
+}
+
+int NSID3v2_Frame_UserText_SetUTF16(nsid3v2_frame_t f, const wchar_t *description, const wchar_t *value)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ size_t value_len = wcslen(value);
+ size_t description_len = wcslen(description);
+ size_t bytes = (value_len + description_len + 1) * 2 + 1; // leave 1 byte for encoding
+
+ size_t datalen;
+ void *data;
+ int ret = frame->NewData(bytes, &data, &datalen);
+ if (ret == NErr_Success)
+ {
+ uint8_t *data8 = (uint8_t *)data;
+ data8[0]=1; // set encoding to UTF-16
+ wcscpy((wchar_t *)(data8+1), description); // guaranteed to be room
+ memcpy(data8+1+1+description_len*2, value, value_len*2);
+ }
+ return ret;
+}
+
+int NSID3v2_Frame_UserText_SetLatin(nsid3v2_frame_t f, const char *description, const char *value)
+{
+ ID3v2::Frame *frame = (ID3v2::Frame *)f;
+ size_t value_len = strlen(value);
+ size_t description_len = strlen(description);
+ size_t bytes = (value_len + description_len + 1) + 1; // leave 1 byte for encoding
+
+ size_t datalen;
+ void *data;
+ int ret = frame->NewData(bytes, &data, &datalen);
+ if (ret == NErr_Success)
+ {
+ uint8_t *data8 = (uint8_t *)data;
+ data8[0]=0; // set encoding to ISO-8859-1
+ strcpy((char *)(data8+1), description); // guaranteed to be room
+ memcpy(data8+1+1+description_len, value, value_len);
+ }
+ return ret;
+}
+#endif
diff --git a/Src/replicant/nsid3v2/windows/nsid3v2.h b/Src/replicant/nsid3v2/windows/nsid3v2.h
new file mode 100644
index 000000000..345f74ca2
--- /dev/null
+++ b/Src/replicant/nsid3v2/windows/nsid3v2.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "foundation/types.h"
+#include "foundation/export.h"
+#include "foundation/error.h"
+#include "nx/nxstring.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NSID3V2_EXPORT
+ typedef struct nsid3v2_header_struct_t { } *nsid3v2_header_t;
+ typedef struct nsid3v2_tag_struct_t { } *nsid3v2_tag_t;
+ typedef struct nsid3v2_frame_struct_t { } *nsid3v2_frame_t;
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nsmp3/LAMEInfo.cpp b/Src/replicant/nsmp3/LAMEInfo.cpp
new file mode 100644
index 000000000..0f7915329
--- /dev/null
+++ b/Src/replicant/nsmp3/LAMEInfo.cpp
@@ -0,0 +1,293 @@
+#include "LAMEInfo.h"
+#include "MPEGHeader.h"
+#include "foundation/error.h"
+#include <string.h>
+#include "nu/ByteReader.h"
+#include "nu/BitReader.h"
+// Xing header -
+// 4 Xing
+// 4 flags
+// 4 frames
+// 4 bytes
+// 100 toc
+// 4 bytes VBR quality
+
+// Lame tag
+// 9 bytes - release name
+// 11
+
+// Lame extended info tag
+
+// http://gabriel.mp3-tech.org/mp3infotag.html
+
+
+
+LAMEInfo::LAMEInfo()
+{
+ memset(this, 0, sizeof(LAMEInfo));
+}
+
+bool LAMEInfo::Flag(int flag) const
+{
+ return flags & flag;
+}
+
+int LAMEInfo::GetGaps(size_t *pregap, size_t *postgap)
+{
+ if (!encoder_delay)
+ return NErr_Empty;
+
+ *pregap = encoder_delay;
+ *postgap = padding;
+ return NErr_Success;
+}
+
+uint64_t LAMEInfo::GetSeekPoint(double percent) const
+{
+ // interpolate in TOC to get file seek point in bytes
+ int a;
+ uint64_t seekpoint;
+ double fa, fb, fx;
+
+ percent*=100.0;
+ if (percent < 0.0)
+ percent = 0.0;
+ if (percent > 100.0)
+ percent = 100.0;
+
+ a = (int)(percent);
+ if (a > 99) a = 99;
+ fa = toc[a];
+ if (a < 99)
+ {
+ fb = toc[a + 1];
+ }
+ else
+ {
+ fb = 256.0;
+ }
+
+ fx = fa + (fb - fa) * (percent - a);
+ seekpoint = (uint64_t) ((1.0 / 256.0) * fx * bytes);
+ return seekpoint;
+}
+
+uint64_t LAMEInfo::GetSamples() const
+{
+ if (flags&FRAMES_FLAG)
+ {
+ uint64_t samples = frames * samples_per_frame;
+ samples -= (encoder_delay + padding);
+ return samples;
+ }
+ return 0;
+}
+
+uint32_t LAMEInfo::GetFrames() const
+{
+ if (flags&FRAMES_FLAG)
+ return frames;
+ else
+ return 0;
+}
+
+double LAMEInfo::GetLengthSeconds() const
+{
+ if (flags&FRAMES_FLAG)
+ {
+ return (double)GetSamples() / (double)sample_rate;
+ }
+ return 0;
+}
+
+int LAMEInfo::Read(const MPEGHeader &frame, const uint8_t *buffer, size_t buffer_length)
+{
+ int flags;
+ bool crc_hack_applied=false;
+ bytereader_value_t byte_reader;
+
+ /* maybe toolame writes these things also, I dunno. we'll just abort for now */
+ if (frame.layer != MPEGHeader::Layer3)
+ return 0;
+
+
+ bytereader_init(&byte_reader, buffer, buffer_length);
+
+ sample_rate = frame.GetSampleRate();
+ version = frame.mpeg_version;
+ samples_per_frame = frame.GetSamplesPerFrame();
+
+ // skip sideinfo
+ if (frame.mpeg_version == MPEGHeader::MPEG1) // MPEG 1
+ {
+ if (frame.channel_mode == MPEGHeader::Mono)
+ bytereader_advance(&byte_reader, 17);
+ else
+ bytereader_advance(&byte_reader, 32);
+ }
+ else if (frame.mpeg_version == MPEGHeader::MPEG2) // MPEG 2
+ {
+ if (frame.channel_mode == MPEGHeader::Mono)
+ bytereader_advance(&byte_reader, 9);
+ else
+ bytereader_advance(&byte_reader, 17);
+ }
+ else if (frame.mpeg_version == MPEGHeader::MPEG2_5) // MPEG 2
+ {
+ if (frame.channel_mode == MPEGHeader::Mono)
+ bytereader_advance(&byte_reader, 9);
+ else
+ bytereader_advance(&byte_reader, 17);
+ }
+
+ if (bytereader_size(&byte_reader) > buffer_length /* check for wraparound */
+ || bytereader_size(&byte_reader) < 8)
+ return NErr_Insufficient;
+
+again:
+ if (bytereader_show_u32_be(&byte_reader) == 'Info')
+ cbr=1;
+ else if (bytereader_show_u32_be(&byte_reader) != 'Xing' && bytereader_show_u32_be(&byte_reader) != 'Lame')
+ {
+ // if there's CRC data, LAME sometimes writes to the wrong position
+ if (frame.IsCRC() && !crc_hack_applied)
+ {
+ crc_hack_applied=true;
+ bytereader_advance(&byte_reader, 2);
+ goto again;
+ }
+ return NErr_False;
+ }
+
+ bytereader_advance(&byte_reader, 4); // skip Xing tag
+ flags = this->flags = bytereader_read_u32_be(&byte_reader);
+
+ if (flags & FRAMES_FLAG)
+ {
+ if (bytereader_size(&byte_reader) < 4)
+ return NErr_Insufficient;
+
+ frames = bytereader_read_u32_be(&byte_reader);
+ }
+ if (flags & BYTES_FLAG)
+ {
+ if (bytereader_size(&byte_reader) < 4)
+ return NErr_Insufficient;
+ bytes = bytereader_read_u32_be(&byte_reader);
+ }
+ if (flags & TOC_FLAG)
+ {
+ if (bytereader_size(&byte_reader) < 100)
+ return NErr_Insufficient;
+
+ int i;
+ memcpy(toc, bytereader_pointer(&byte_reader), 100);
+
+ // verify that TOC isn't empty
+ for (i = 0; i < 100; i++)
+ if (toc[i]) break;
+ if (i == 100)
+ flags &= ~TOC_FLAG;
+
+ bytereader_advance(&byte_reader, 100);
+ }
+
+ vbr_scale = -1;
+ if (flags & VBR_SCALE_FLAG)
+ {
+ if (bytereader_size(&byte_reader) < 4)
+ return NErr_Insufficient;
+ vbr_scale = bytereader_read_u32_be(&byte_reader);
+ }
+
+ if (bytereader_size(&byte_reader) < 27)
+ return NErr_Success; // stop here if we have to, we have at least some data
+
+ if (bytereader_show_u32_be(&byte_reader) == 'LAME')
+ {
+ for (int i=0;i<9;i++)
+ encoder[i]=bytereader_read_u8(&byte_reader);
+ encoder[9]=0; // null terminate in case tag used all 9 characters
+
+ if (bytereader_show_u8(&byte_reader) == '(')
+ {
+ // read 11 more characters
+ for (int i=9;i<20;i++)
+ encoder[i]=bytereader_read_u8(&byte_reader);
+ encoder[20]=0;
+ }
+ else
+ {
+ tag_revision = bytereader_show_u8(&byte_reader)>>4;
+ if (tag_revision == 0)
+ {
+ encoding_method = bytereader_read_u8(&byte_reader)&0xF; // VBR method
+ lowpass = bytereader_read_u8(&byte_reader)*100; // lowpass value
+ peak=bytereader_read_f32_be(&byte_reader); // read peak value
+
+ // read track gain
+ int16_t gain_word = bytereader_read_s16_be(&byte_reader);
+ if ((gain_word & 0xFC00) == 0x2C00)
+ {
+ replaygain_track_gain = (float)(gain_word & 0x01FF);
+ replaygain_track_gain /= 10;
+ if (gain_word & 0x0200)
+ replaygain_track_gain = -replaygain_track_gain;
+ }
+
+ // read album gain
+ gain_word = bytereader_read_s16_be(&byte_reader);
+ if ((gain_word & 0xFC00) == 0x4C00)
+ {
+ replaygain_album_gain = (float)(gain_word & 0x01FF);
+ replaygain_album_gain /= 10;
+ if (gain_word & 0x0200)
+ replaygain_album_gain = -replaygain_album_gain;
+ }
+
+ bytereader_advance(&byte_reader, 1); // skip encoding flags + ATH type
+ abr_bitrate = bytereader_read_u8(&byte_reader); // bitrate
+
+ // get the encoder delay and padding, annoyingly as 12 bit values packed into 3 bytes
+ BitReader bit_reader;
+ bit_reader.data = (const uint8_t *)bytereader_pointer(&byte_reader);
+ bit_reader.numBits = 24;
+ const uint8_t *temp = (const uint8_t *)bytereader_pointer(&byte_reader);
+ encoder_delay = bit_reader.getbits(12);
+ padding = bit_reader.getbits(12);
+ bytereader_advance(&byte_reader, 3);
+
+ bytereader_advance(&byte_reader, 4);
+ // skip misc
+ // skip MP3Gain reconstruction info
+ // skip surround info and preset info
+
+ music_length = bytereader_read_u32_be(&byte_reader);
+ music_crc = bytereader_read_u16_be(&byte_reader);
+ tag_crc = bytereader_read_u16_be(&byte_reader);
+
+ }
+ }
+ }
+ else if (!memcmp(bytereader_pointer(&byte_reader), "iTunes", 6))
+ {
+ int i=0;
+ while (bytereader_size(&byte_reader) && i < 31)
+ {
+ encoder[i] = bytereader_read_u8(&byte_reader);
+ if (!encoder[i])
+ break;
+ i++;
+ }
+ encoder[31]=0;
+ }
+ else if (!memcmp(bytereader_pointer(&byte_reader), "\0\0\0\0mp3HD", 9))
+ {
+ bytereader_advance(&byte_reader, 4);
+ for (int i=0;i<5;i++)
+ encoder[i] = bytereader_read_u8(&byte_reader);
+
+ encoder[5]=0;
+ }
+ return NErr_Success;
+}
diff --git a/Src/replicant/nsmp3/LAMEInfo.h b/Src/replicant/nsmp3/LAMEInfo.h
new file mode 100644
index 000000000..4ed8d6669
--- /dev/null
+++ b/Src/replicant/nsmp3/LAMEInfo.h
@@ -0,0 +1,66 @@
+#pragma once
+#include "foundation/types.h"
+#include "MPEGHeader.h"
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
+#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
+
+struct LAMEInfo
+{
+ LAMEInfo();
+ uint64_t GetSeekPoint(double percent) const; /* 0 <= percent <= 1.0 */
+ int Read(const MPEGHeader &frame, const uint8_t *buffer, size_t bufferlen);
+ double GetLengthSeconds() const;
+ uint64_t GetSamples() const;
+ uint32_t GetFrames() const;
+
+ bool Flag(int flag) const;
+ int GetGaps(size_t *pregap, size_t *postgap);
+protected:
+ int version;
+ int sample_rate;
+ int samples_per_frame;
+
+ int cbr; // set to 1 if the file is actually just CBR
+ // Xing
+ int flags; // from Xing header data
+ uint32_t frames; // total bit stream frames from Xing header data
+ uint64_t bytes; // total bit stream bytes from Xing header data
+ int vbr_scale; // encoded vbr scale from Xing header data
+ uint8_t toc[100]; // pointer to unsigned char toc_buffer[100]
+ // may be NULL if toc not desired
+
+ // LAME
+ char encoder[32]; // 9 characters, but we'll add an extra NULL just in case
+ float peak;
+ float replaygain_album_gain;
+ float replaygain_track_gain;
+ unsigned short lowpass;
+ unsigned short encoder_delay;
+ unsigned short padding;
+ uint8_t encoding_method;
+ uint8_t tag_revision;
+ uint8_t abr_bitrate;
+ uint32_t music_length;
+ uint16_t music_crc;
+ uint16_t tag_crc;
+};
+
+enum
+{
+ ENCODING_METHOD_LAME = 0,
+ ENCODING_METHOD_CBR = 1,
+ ENCODING_METHOD_ABR = 2,
+ ENCODING_METHOD_VBR1 = 3,
+ ENCODING_METHOD_VBR2 = 4,
+ ENCODING_METHOD_VBR3 = 5,
+ ENCODING_METHOD_VBR4 = 6,
+ ENCODING_METHOD_CBR_2PASS = 8,
+ ENCODING_METHOD_ABR_2PASS = 9,
+};
+
+int ReadLAMEInfo(const MPEGHeader &frame, const uint8_t *buffer, LAMEInfo *lameInfo);
+
diff --git a/Src/replicant/nsmp3/MPEGHeader.cpp b/Src/replicant/nsmp3/MPEGHeader.cpp
new file mode 100644
index 000000000..1971577ec
--- /dev/null
+++ b/Src/replicant/nsmp3/MPEGHeader.cpp
@@ -0,0 +1,170 @@
+#include "MPEGHeader.h"
+#include <math.h>
+
+// [mpeg_version][layer][index]
+static const int bitrates[4][4][15] =
+{
+ {
+ // MPEG-2.5
+ { 0,},
+ { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 3
+ { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 2
+ { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000}, // Layer 1
+ },
+
+ {
+ // invalid
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ },
+
+ {
+ // MPEG-2
+ { 0,},
+ { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 3
+ { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 2
+ { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000}, // Layer 1
+ },
+
+ {
+ // MPEG-1
+ { 0,},
+ { 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000}, // Layer 3
+ { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000}, // Layer 2
+ { 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000}, // Layer 1
+ },
+};
+
+// [mpeg_version][index]
+static const int sample_rates[4][4] =
+{
+ {11025, 12000, 8000, 0}, // MPEG-2.5
+ {0, },
+ {22050, 24000, 16000, 0}, // MPEG-2
+ {44100, 48000, 32000, 0}, // MPEG-1
+};
+
+// [mpeg_version][layer]
+static const int samples_per_frame[4][4] =
+{
+ // Layer 3, Layer 2, Layer 1
+ { 0, 576, 1152, 384}, // MPEG2.5
+ { 0, },
+ { 0, 576, 1152, 384}, // MPEG2
+ { 0, 1152, 1152, 384}, // MPEG1
+};
+
+// [layer]
+static const int bits_per_slot[4] = { 0, 8, 8, 32 };
+
+void MPEGHeader::ReadBuffer(const uint8_t *buffer)
+{
+ sync = ((uint16_t)buffer[0] << 3) | (buffer[1] >> 5);
+ mpeg_version = (buffer[1] >> 3) & 3;
+ layer = (buffer[1] >> 1) & 3;
+ protection = (buffer[1]) & 1;
+ bitrate_index = (buffer[2] >> 4) & 0xF;
+ sample_rate_index = (buffer[2] >> 2) & 3;
+ padding_bit = (buffer[2] >> 1) & 1;
+ private_bit = buffer[2] & 1;
+ channel_mode = (buffer[3] >> 6) & 3;
+ mode_extension = (buffer[3] >> 4) & 3;
+ copyright = (buffer[3] >> 3) & 1;
+ original = (buffer[3] >> 2) & 1;
+ emphasis = (buffer[3]) & 3;
+}
+
+bool MPEGHeader::IsSync() const
+{
+ return sync == 0x07FF
+ && layer != LayerError
+ && mpeg_version != MPEG_Error
+ && bitrate_index != 15
+ && bitrate_index != 0
+ && sample_rate_index != 3
+ && !(mpeg_version == MPEG2 && layer != Layer3)
+ && !(mpeg_version == MPEG2_5 && layer != Layer3);
+}
+
+int MPEGHeader::GetBitrate() const
+{
+ return bitrates[mpeg_version][layer][bitrate_index];
+}
+
+int MPEGHeader::HeaderSize() const
+{
+ if (protection == CRC)
+ return 4 + 2; // 32bits frame header, 16bits CRC
+ else
+ return 4; // 32bits frame ehader
+}
+
+int MPEGHeader::GetSampleRate() const
+{
+ return sample_rates[mpeg_version][sample_rate_index];
+}
+
+bool MPEGHeader::IsCopyright() const
+{
+ return copyright == 1;
+}
+bool MPEGHeader::IsCRC() const
+{
+ return protection == CRC;
+}
+
+bool MPEGHeader::IsOriginal() const
+{
+ return original == 1;
+}
+
+int MPEGHeader::GetSamplesPerFrame() const
+{
+ return samples_per_frame[mpeg_version][layer];
+}
+
+int MPEGHeader::FrameSize() const
+{
+ int nBitsPerSlot;
+ int nAvgSlotsPerFrame;
+
+ nBitsPerSlot = bits_per_slot[layer];
+
+ nAvgSlotsPerFrame = (GetSamplesPerFrame() * (bitrates[mpeg_version][layer][bitrate_index] / nBitsPerSlot)) / sample_rates[mpeg_version][sample_rate_index];
+
+ return (nAvgSlotsPerFrame + padding_bit) * nBitsPerSlot / 8;
+}
+
+int MPEGHeader::GetLayer() const
+{
+ switch(layer)
+ {
+ case Layer1:
+ return 1;
+ case Layer2:
+ return 2;
+ case Layer3:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+int MPEGHeader::GetNumChannels() const
+{
+ switch(channel_mode)
+ {
+ case Stereo:
+ return 2;
+ case JointStereo:
+ return 2;
+ case DualChannel:
+ return 2;
+ case Mono:
+ return 1;
+ default:
+ return 0;
+ }
+}
diff --git a/Src/replicant/nsmp3/MPEGHeader.h b/Src/replicant/nsmp3/MPEGHeader.h
new file mode 100644
index 000000000..d69271e12
--- /dev/null
+++ b/Src/replicant/nsmp3/MPEGHeader.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "foundation/types.h"
+
+class MPEGHeader
+{
+public:
+ void ReadBuffer(const uint8_t *buffer);
+ int GetNumChannels() const;
+ bool IsSync() const;
+ int GetBitrate() const;
+ int HeaderSize() const;
+ int GetSampleRate() const;
+ int FrameSize() const;
+ int GetLayer() const;
+ bool IsCRC() const;
+ bool IsCopyright() const;
+ bool IsOriginal() const;
+ int GetSamplesPerFrame() const;
+ enum
+ {
+ NotPadded=0,
+ Padded=1,
+ CRC = 0,
+ NoProtection = 1,
+ Stereo = 0,
+ JointStereo = 1,
+ DualChannel = 2,
+ Mono = 3,
+ MPEG1 = 3,
+ MPEG2 = 2,
+ MPEG_Error = 1,
+ MPEG2_5 = 0,
+ Layer1 = 3,
+ Layer2 = 2,
+ Layer3 = 1,
+ LayerError = 0,
+ Emphasis_None = 0,
+ Emphasis_50_15_ms = 1,
+ Emphasis_reserved = 2,
+ Emphasis_CCIT_J_17 = 3,
+ };
+
+ uint16_t sync;
+ uint8_t mpeg_version, layer, protection, bitrate_index;
+ uint8_t padding_bit, private_bit, channel_mode, mode_extension;
+ uint8_t sample_rate_index, copyright, original, emphasis;
+};
diff --git a/Src/replicant/nsmp3/OFL.cpp b/Src/replicant/nsmp3/OFL.cpp
new file mode 100644
index 000000000..247992aaf
--- /dev/null
+++ b/Src/replicant/nsmp3/OFL.cpp
@@ -0,0 +1,165 @@
+#include "OFL.h"
+#include "foundation/error.h"
+
+static void crcofl(unsigned short crcPoly, unsigned short crcMask, unsigned long *crc, unsigned char byte)
+{
+ int i;
+ for (i=0; i<8; i++)
+ {
+ unsigned short flag = (*crc) & crcMask ? 1:0;
+ flag ^= (byte & 0x80 ? 1 : 0);
+ (*crc)<<=1;
+ byte <<= 1;
+ if(flag)
+ (*crc) ^= crcPoly;
+ }
+}
+
+int OFL::GetGaps(size_t *pregap, size_t *postgap)
+{
+ /* TODO: verify the postgap calculation */
+ if (codec_delay >= 529)
+ {
+ *pregap = codec_delay;
+ size_t endcut;
+ endcut = samples_per_frame - ((total_length + codec_delay) % samples_per_frame); // how many 0 samples had to be added?
+ *postgap = endcut;
+ return NErr_Success;
+ }
+ return NErr_Empty;
+}
+
+
+double OFL::GetLengthSeconds() const
+{
+ return (double)GetSamples() / (double)sample_rate;
+}
+
+uint64_t OFL::GetSamples() const
+{
+ return total_length;
+}
+
+uint32_t OFL::GetFrames() const
+{
+ uint64_t real_samples = (total_length+codec_delay)*samples_per_frame;
+ return (uint32_t) (real_samples/samples_per_frame);
+}
+
+int OFL::Read(const MPEGHeader &header, const uint8_t *buffer, size_t buffer_len)
+{
+ if (header.layer != MPEGHeader::Layer3)
+ return NErr_False;
+
+ sample_rate = header.GetSampleRate();
+ samples_per_frame = header.GetSamplesPerFrame();
+
+ if (header.channel_mode == MPEGHeader::Mono)
+ {
+ if (header.mpeg_version == MPEGHeader::MPEG1)
+ {
+ // 0-9 : main_data_end
+ int16_t main_data_end = (buffer[0] << 1) | (buffer[1] >> 7);
+
+ // read the 2 part2_3_lengths out so we know how big the main data section is
+ uint16_t part2_3_length = ((buffer[2] & 0x3F) << 6) | (buffer[3]>>2); // bits 18-30
+ part2_3_length += ((buffer[9] & 0x7) << 9) | (buffer[10] << 1) | (buffer[11] >> 7) ; // bits 77-89
+
+ size_t offset = 17 + (part2_3_length+7)/8;
+ if (offset+9 < buffer_len && buffer[offset] == 0xb4)
+ {
+ unsigned long crc=255;
+ for (int i=0;i<9;i++)
+ crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
+
+ if ((crc & 0xFF) == buffer[offset+9])
+ {
+ total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
+ codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
+ additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
+ return NErr_Success;
+ }
+ }
+ }
+ else
+ { // MPEG2 and 2.5
+ // 0-8 : main_data_end
+ uint16_t main_data_end = buffer[0];
+
+ // read the 2 part2_3_lengths out so we know how big the main data section is
+ uint16_t part2_3_length = ((buffer[1] & 0x7F) << 5) | (buffer[2]>>3); // bits 9-21
+
+ size_t offset = 9 + (part2_3_length+7)/8;
+ if (offset+9 < buffer_len && buffer[offset] == 0xb4)
+ {
+ unsigned long crc=255;
+ for (int i=0;i<9;i++)
+ crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
+
+ if ((crc & 0xFF) == buffer[offset+9])
+ {
+ total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
+ codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
+ additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
+ return NErr_Success;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (header.mpeg_version == MPEGHeader::MPEG1)
+ {
+ // 0-9 : main_data_end
+ uint16_t main_data_end = (buffer[0] << 1) | (buffer[1] >> 7);
+
+ // read the 4 part2_3_lengths out so we know how big the main data section is
+ uint16_t part2_3_length = ((buffer[2] & 0xF) << 8) | buffer[3]; // bits 20-32
+ part2_3_length += ((buffer[9] & 0x1) << 11) | (buffer[10] << 3) | (buffer[11] >> 5) ; // bits 79-91
+ part2_3_length += ((buffer[17] & 0x3F) << 6) | (buffer[18] >> 2); // bits 138-150
+ part2_3_length += ((buffer[24] & 0x7) << 9) | (buffer[25] << 1) | (buffer[26] >> 7); // bits 197-209
+
+ size_t offset = 32 + (part2_3_length+7)/8;
+ if (offset+9 < buffer_len && buffer[offset] == 0xb4)
+ {
+ unsigned long crc=255;
+ for (int i=0;i<9;i++)
+ crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
+
+ if ((crc & 0xFF) == buffer[offset+9])
+ {
+ total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
+ codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
+ additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
+ return NErr_Success;
+ }
+ }
+ }
+ else
+ { // MPEG2 and 2.5
+ // 0-8 : main_data_end
+ uint16_t main_data_end = buffer[0];
+
+ // read the 4 part2_3_lengths out so we know how big the main data section is
+ uint16_t part2_3_length = ((buffer[1] & 0x3F) << 6) | (buffer[2] >> 2); // bits 10-22
+ part2_3_length += ((buffer[8] & 0x7) << 9) | (buffer[9] << 1) | (buffer[10] >> 7) ; // bits 69-81
+
+ size_t offset = 17 + (part2_3_length+7)/8;
+ if (offset+9 < buffer_len && buffer[offset] == 0xb4)
+ {
+ unsigned long crc=255;
+ for (int i=0;i<9;i++)
+ crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
+
+ if ((crc & 0xFF) == buffer[offset+9])
+ {
+ total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
+ codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
+ additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
+ return NErr_Success;
+ }
+ }
+ }
+ }
+ return NErr_False;
+}
diff --git a/Src/replicant/nsmp3/OFL.h b/Src/replicant/nsmp3/OFL.h
new file mode 100644
index 000000000..4d3e08c24
--- /dev/null
+++ b/Src/replicant/nsmp3/OFL.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "MPEGHeader.h"
+class OFL
+{
+public:
+ int Read(const MPEGHeader &header, const uint8_t *buffer, size_t buffer_len);
+ double GetLengthSeconds() const;
+ uint64_t GetSamples() const;
+ uint32_t GetFrames() const;
+ int GetGaps(size_t *pregap, size_t *postgap);
+
+private:
+ int samples_per_frame;
+ uint32_t total_length;
+ uint16_t codec_delay;
+ uint16_t additional_delay;
+
+ unsigned int sample_rate;
+};
+
diff --git a/Src/replicant/nsmp3/nsmp3.sln b/Src/replicant/nsmp3/nsmp3.sln
new file mode 100644
index 000000000..ab85989ce
--- /dev/null
+++ b/Src/replicant/nsmp3/nsmp3.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsmp3", "nsmp3.vcxproj", "{422DBC5C-A877-4023-8918-4CDF8068DDA1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|Win32.Build.0 = Debug|Win32
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|x64.ActiveCfg = Debug|x64
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|x64.Build.0 = Debug|x64
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|Win32.ActiveCfg = Release|Win32
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|Win32.Build.0 = Release|Win32
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|x64.ActiveCfg = Release|x64
+ {422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1E476B03-144A-407A-91B6-F40A1A24357F}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nsmp3/nsmp3.vcxproj b/Src/replicant/nsmp3/nsmp3.vcxproj
new file mode 100644
index 000000000..246e2114f
--- /dev/null
+++ b/Src/replicant/nsmp3/nsmp3.vcxproj
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{422DBC5C-A877-4023-8918-4CDF8068DDA1}</ProjectGuid>
+ <RootNamespace>nsmp3</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x86_Debug\</OutDir>
+ <IntDir>x86_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x64_Debug\</OutDir>
+ <IntDir>x64_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x86_Release\</OutDir>
+ <IntDir>x86_Release\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x64_Release\</OutDir>
+ <IntDir>x64_Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="LAMEInfo.cpp" />
+ <ClCompile Include="MPEGHeader.cpp" />
+ <ClCompile Include="OFL.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="LAMEInfo.h" />
+ <ClInclude Include="MPEGHeader.h" />
+ <ClInclude Include="OFL.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nsmp3dec/bitsequence.h b/Src/replicant/nsmp3dec/bitsequence.h
new file mode 100644
index 000000000..df7b78b6e
--- /dev/null
+++ b/Src/replicant/nsmp3dec/bitsequence.h
@@ -0,0 +1,69 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: bitsequence.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1997-12-23
+ * contents/description: HEADER - bitsequence object
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:01 $
+ * $Id: bitsequence.h,v 1.1 2010/11/17 20:46:01 audiodsp Exp $
+ */
+
+#ifndef __BITSEQUENCE_H__
+#define __BITSEQUENCE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "bitstream.h"
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+//
+// Bitstream parser class.
+//
+// This helper class is basically a numerical value that can read itself from
+// a CBitStream interface for convenience. The decoder almost completely
+// does the bitstream parsing through CBitSequence rather than CBitStream
+// directly.
+//
+
+class CBitSequence
+{
+public:
+
+ CBitSequence(int nBits = 0) { m_nBits = nBits; m_nValue = 0; }
+ virtual ~CBitSequence() {}
+
+ void SetNumberOfBits(int nBits) { m_nBits = nBits; }
+ int GetNumberOfBits() const { return m_nBits; }
+
+ bool ReadFrom(CBitStream &Bs) { m_nValue = Bs.GetBits(m_nBits); return true; }
+ bool ReadFrom_Bit(CBitStream &Bs) { m_nValue = Bs.Get1Bit(); return true; }
+ bool ReadFrom(CBitStream &Bs, int nBits) { SetNumberOfBits(nBits); return ReadFrom(Bs); }
+
+ bool Equals(int nValue) const { return (m_nValue == nValue); }
+
+ int ToInt() const { return m_nValue; }
+ void FromInt(int nValue) { m_nValue = nValue; }
+
+protected:
+
+private:
+
+ int m_nBits;
+ int m_nValue;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/bitstream.h b/Src/replicant/nsmp3dec/bitstream.h
new file mode 100644
index 000000000..c72e43580
--- /dev/null
+++ b/Src/replicant/nsmp3dec/bitstream.h
@@ -0,0 +1,103 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: bitstream.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1997-12-05
+ * contents/description: generic bitbuffer - HEADER
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/18 18:22:02 $
+ * $Id: bitstream.h,v 1.4 2011/01/18 18:22:02 audiodsp Exp $
+ */
+
+#ifndef __BITSTREAM_H__
+#define __BITSTREAM_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+class CGioBase;
+
+/*-------------------------------------------------------------------------*/
+
+//
+// Bitstream input class.
+//
+// This class defines the interface that the mp3 decoder object will
+// read all of its bitstream input data from.
+//
+
+class CBitStream
+{
+public:
+
+ CBitStream(int cbSize);
+ CBitStream(unsigned char *pBuf, int cbSize, bool fDataValid = false);
+ virtual ~CBitStream();
+
+ virtual void Reset();
+ bool ByteAligned() const { return !(m_BitNdx & 7); }
+ bool ResetOccurred() { return m_ResetOccurred; }
+ void SetResetState(bool state) { m_ResetOccurred = state; }
+
+ void Connect(CGioBase *pGB);
+
+ void ResetBitCnt() { m_BitCnt = 0; }
+ int GetBitCnt() const { return m_BitCnt; }
+
+ unsigned int GetBits(unsigned int nBits); // gets 16 bits or less
+ unsigned int GetBits8(unsigned int nBits); // gets 8 bits or less
+ unsigned int Get1Bit();
+ unsigned long Get32Bits();
+
+ bool Ff(int nBits) { return ( (nBits > 0) ? Seek(nBits) : false); }
+ bool Rewind(int nBits) { return ( (nBits > 0) ? Seek(-nBits) : false); }
+ bool Seek(int nBits)
+ {
+ m_BitCnt += nBits;
+ m_ValidBits -= nBits;
+ m_BitNdx = (m_BitNdx+nBits) & m_bitMask;
+ return true;
+ }
+
+ int GetValidBits() const { return m_ValidBits; }
+ int GetFree() const;
+
+ void SetEof();
+ int Fill(const unsigned char *pBuf, int cbSize);
+ int Fill(CBitStream &Bs, int cbSize);
+
+protected:
+
+ int Refill();
+ bool IsEof() const;
+ bool IsConnected() const;
+
+private:
+
+ CGioBase *m_pGB; // I/O object
+ int m_nBytes; // size of buffer in bytes
+ int m_mask;
+ int m_nBits; // size of buffer in bits
+ int m_bitMask;
+ int m_ValidBits; // valid bits in buffer
+ int m_ReadOffset; // where to write next
+ int m_BitCnt; // bit counter
+ int m_BitNdx; // position of next bit in byte
+ bool m_fEof; // indication of input eof
+ unsigned char *m_Buf; // the buffer
+ bool m_fBufferIntern; // did we allocate the buffer ourselves
+ bool m_ResetOccurred; // reset just occurred, only for dynamic buffer used
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/conceal.h b/Src/replicant/nsmp3dec/conceal.h
new file mode 100644
index 000000000..c8408f233
--- /dev/null
+++ b/Src/replicant/nsmp3dec/conceal.h
@@ -0,0 +1,137 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: conceal.h
+ * project : ISO/MPEG-Decoder
+ * author : Stefan Gewinner
+ * date : 1998-05-26
+ * contents/description: error concealment class - HEADER
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:02 $
+ * $Id: conceal.h,v 1.1 2010/11/17 20:46:02 audiodsp Exp $
+ */
+
+#ifndef __CONCEAL_H__
+#define __CONCEAL_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+
+/*-------------------------------------------------------------------------*/
+
+//
+// Error concealment class.
+//
+// This object is used to apply error concealment to a spectrum in case of
+// CRC errors. CRC protection is optional for ISO/MPEG bitstreams.
+//
+
+class CErrorConcealment
+{
+public:
+
+ CErrorConcealment();
+ ~CErrorConcealment() {}
+
+ void Init();
+
+ void Apply
+ (
+ bool fApply, // true: restore, false: store
+ const MPEG_INFO &Info,
+ MP3SI &Si,
+ float *lpSpec,
+ int gr,
+ int ch
+ );
+
+ enum { MAX_SPECTRUM_DATA = 4 };
+
+protected :
+
+ //
+ // structure to hold information for one granule
+ //
+ typedef struct tagGRAN_DATA
+ {
+ MP3SI_GRCH gr; /* side info */
+ float Rs[SBLIMIT*SSLIMIT]; /* line amplitudes */
+ float nrg[23]; /* sf-band energies */
+ int nrgValid; /* valid-flag for sf-band energies */
+
+#ifdef DEBUG_CONCEALMENT
+ long frameNumber;
+#endif
+ } GRAN_DATA;
+
+ //
+ // structure for concealment data
+ //
+ typedef struct tagSPECTRUM_DATA
+ {
+ int writeOffset; /* place to store next valid granule */
+ GRAN_DATA gran[MAX_SPECTRUM_DATA]; /* ring buffer */
+ GRAN_DATA estGran;
+ } SPECTRUM_DATA;
+
+ SPECTRUM_DATA SpecDataBuffer[2]; /* one buffer for each channel */
+
+ void Store
+ (
+ const MPEG_INFO &Info,
+ const MP3SI &Si,
+ const float *lpSpec,
+ int gr,
+ int ch
+ );
+
+ void Restore
+ (
+ const MPEG_INFO &Info,
+ MP3SI &Si,
+ float *lpSpec,
+ int gr,
+ int ch
+ );
+
+#ifdef USE_ENERGY_PREDICTION
+ float predict(const float *hist, const float *coff, int n);
+ void adaptPredictor(const float *hist, float pwr, float *coff, float d, int n);
+#endif
+
+ void estimateBandEnergies(const MPEG_INFO &Info, GRAN_DATA *g);
+ void predictEnergies(const MPEG_INFO &Info, SPECTRUM_DATA *s);
+
+
+ //
+ // random seeds for the float and bit random generators
+ //
+ float ranHigh1(float a);
+ float ranHigh2(float a);
+ float ranLow(float a);
+ float ran3(long *idum);
+ int irbit2(unsigned long *iseed);
+
+ int inext;
+ int inextp;
+ long ma [56];
+ int iff ;
+
+ long f_seed, w_seed ;
+ unsigned long b_seed ;
+
+#ifdef DEBUG_CONCEALMENT
+ long currentFrame ;
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/crc16.h b/Src/replicant/nsmp3dec/crc16.h
new file mode 100644
index 000000000..a8deb38ee
--- /dev/null
+++ b/Src/replicant/nsmp3dec/crc16.h
@@ -0,0 +1,35 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: crc16.h
+ * project : ISO/MPEG decoder
+ * author : Martin Sieler
+ * date : 1998-05-26
+ * contents/description: functions to calculate a CRC-16
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:02 $
+ * $Id: crc16.h,v 1.1 2010/11/17 20:46:02 audiodsp Exp $
+ */
+
+#ifndef __CRC16_H__
+#define __CRC16_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/* ------------------------------------------------------------------------*/
+
+class CBitStream;
+
+/* ------------------------------------------------------------------------*/
+
+unsigned int CalcCrc(CBitStream &Bs, int len, unsigned int start);
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/giobase.h b/Src/replicant/nsmp3dec/giobase.h
new file mode 100644
index 000000000..c30aab26d
--- /dev/null
+++ b/Src/replicant/nsmp3dec/giobase.h
@@ -0,0 +1,46 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: giobase.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1998-02-11
+ * contents/description: HEADER - basic I/O class for MPEG Decoder
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:02 $
+ * $Id: giobase.h,v 1.1 2010/11/17 20:46:02 audiodsp Exp $
+ */
+
+#ifndef __GIOBASE_H__
+#define __GIOBASE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mp3sscdef.h"
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+class CGioBase
+{
+public:
+
+ virtual SSC Read(void *pBuffer, int cbToRead, int *pcbRead) = 0;
+ virtual bool IsEof() const = 0;
+
+protected:
+ ~CGioBase() {}
+private:
+
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/huffdec.h b/Src/replicant/nsmp3dec/huffdec.h
new file mode 100644
index 000000000..069db7c0e
--- /dev/null
+++ b/Src/replicant/nsmp3dec/huffdec.h
@@ -0,0 +1,57 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: huffdec.h
+ * project : ISO/MPEG Decoder
+ * author : Martin Sieler
+ * date : 1998-05-26
+ * contents/description: main hufman decoding - HEADER
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:02 $
+ * $Id: huffdec.h,v 1.1 2010/11/17 20:46:02 audiodsp Exp $
+ */
+
+#ifndef __HUFFDEC_H__
+#define __HUFFDEC_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+#include "huffmandecoder.h"
+
+/* ------------------------------------------------------------------------*/
+
+//
+// MPEG Layer-3 huffman decoding class.
+//
+// This class is derived from a CHuffmanDecoder object. In addition to
+// the CHuffmanDecoder object, this object calculates the number of
+// spectral lines in the big value area, the number of spectral lines in the
+// count-one area and the region boundaries within the big value area from
+// an MPEG Layer-3 bitstream sideinfo.
+//
+
+class CMp3Huffman : protected CHuffmanDecoder
+{
+public:
+ CMp3Huffman() {}
+ ~CMp3Huffman() {}
+
+ void Read
+ (
+ CBitStream &Bs, // where to read from
+ int *pISpectrum, // pointer to spectrum
+ MP3SI_GRCH &SiGrCh, // side info (granule/channel)
+ const MPEG_INFO &Info // mpeg info
+ );
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/huffmanbitobj.h b/Src/replicant/nsmp3dec/huffmanbitobj.h
new file mode 100644
index 000000000..21f624d12
--- /dev/null
+++ b/Src/replicant/nsmp3dec/huffmanbitobj.h
@@ -0,0 +1,54 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: huffmanbitobj.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1997-12-29
+ * contents/description: HEADER - Huffman Bit Object
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/18 23:00:53 $
+ * $Id: huffmanbitobj.h,v 1.3 2011/01/18 23:00:53 audiodsp Exp $
+ */
+
+#ifndef __HUFFMANBITOBJ_H__
+#define __HUFFMANBITOBJ_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+class CBitStream;
+class CHuffmanTable;
+
+/*-------------------------------------------------------------------------*/
+
+//
+// Class holding one huffman value.
+//
+// This object reads and decodes one huffman value from a CBitStream
+// object. One huffman value represents either two (big value part) or four
+// spectral lines (count-one part).
+//
+
+class CHuffmanBitObj
+{
+public:
+ CHuffmanBitObj(const CHuffmanTable &HT);
+ virtual ~CHuffmanBitObj();
+
+ int ReadFrom(CBitStream &BS) const;
+
+private:
+ const CHuffmanTable& m_HuffmanTable;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/huffmandecoder.h b/Src/replicant/nsmp3dec/huffmandecoder.h
new file mode 100644
index 000000000..5c4d46152
--- /dev/null
+++ b/Src/replicant/nsmp3dec/huffmandecoder.h
@@ -0,0 +1,85 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: huffmandecoder.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1998-02-08
+ * contents/description: HEADER - huffman decoder
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/21 22:25:58 $
+ * $Id: huffmandecoder.h,v 1.4 2011/01/21 22:25:58 audiodsp Exp $
+ */
+
+#ifndef __HUFFMANDECODER_H__
+#define __HUFFMANDECODER_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "bitsequence.h"
+#include "huffmanbitobj.h"
+#include "huffmantable.h"
+
+/*-------------------------- defines --------------------------------------*/
+
+class CBitStream;
+
+/*-------------------------------------------------------------------------*/
+
+//
+// Huffman decoder (helper) class.
+//
+// This object reads and decodes MPEG Layer-3 huffman data.
+//
+
+class CHuffmanDecoder
+{
+public:
+ CHuffmanDecoder();
+ virtual ~CHuffmanDecoder();
+
+ int ReadHuffmanCode(CBitStream &Bs,
+ int *pIsp,
+ const int *pTableSelect,
+ const int *pRegionEnd,
+ int Count1TableSelect,
+ int Part2_3Length);
+
+protected:
+
+private:
+ int ReadBigValues(CBitStream &Bs,
+ int *pIsp,
+ const int *pTableSelect,
+ const int *pRegionEnd);
+
+ int ReadCount1Area(CBitStream &Bs,
+ int *pIsp,
+ int Count1TableSelect,
+ int Count1Start,
+ int Part2_3Length);
+#ifdef _MSC_VER
+ // these only have one caller and inlining shows notable improvements in the profiler
+ __forceinline void ReadHuffmanDual (CBitStream &Bs, int *pIsp);
+ __forceinline void ReadHuffmanDualLin(CBitStream &Bs, int *pIsp);
+ __forceinline bool ReadHuffmanQuad (CBitStream &Bs, int *pIsp);
+#else
+ void ReadHuffmanDual (CBitStream &Bs, int *pIsp);
+ void ReadHuffmanDualLin(CBitStream &Bs, int *pIsp);
+ bool ReadHuffmanQuad (CBitStream &Bs, int *pIsp);
+#endif
+
+ CHuffmanTable m_HuffmanTable;
+ CHuffmanBitObj m_HuffmanBitObj;
+ CBitSequence m_LinBits;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/huffmantable.h b/Src/replicant/nsmp3dec/huffmantable.h
new file mode 100644
index 000000000..b8e7a9a79
--- /dev/null
+++ b/Src/replicant/nsmp3dec/huffmantable.h
@@ -0,0 +1,102 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: huffmantable.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1998-01-05
+ * contents/description: HEADER - huffman table object
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/18 23:00:53 $
+ * $Id: huffmantable.h,v 1.3 2011/01/18 23:00:53 audiodsp Exp $
+ */
+
+#ifndef __HUFFMANTABLE_H__
+#define __HUFFMANTABLE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+#define HUFFMAN_BITS_4
+
+/*-------------------------------------------------------------------------*/
+
+// Huffman tables.
+//
+// This object holds the huffman table for ISO/MPEG Layer-3.
+//
+ typedef struct
+ {
+ const unsigned char length;
+ const unsigned char value;
+ } huffman_entry_t;
+
+class CHuffmanTable
+{
+public:
+
+ /*unsigned int nTableIndex; */
+ CHuffmanTable();
+ virtual ~CHuffmanTable();
+
+ void SetTableIndex(unsigned int _nTableIndex)
+ { nTableIndex = _nTableIndex; }
+
+ unsigned int GetBitsPerLevel() const
+ { return BITS_PER_LEVEL; }
+
+ unsigned int GetLinBits() const
+ { return ht[nTableIndex].linbits; }
+
+ unsigned char GetCode (unsigned int nIndex, unsigned int nValue) const
+ { return (ht[nTableIndex].table[nIndex][nValue] & 0xff); }
+
+ unsigned char GetLength(unsigned int nIndex, unsigned int nValue) const
+ { return ((ht[nTableIndex].table[nIndex][nValue] >> 8) & 0xff); }
+
+ bool IsTableValid() const
+ { return (ht[nTableIndex].table ? true:false); }
+
+ bool IsLengthZero(unsigned int nIndex, unsigned int nValue) const
+ { return ((ht[nTableIndex].table[nIndex][nValue] & 0xff00) == 0); }
+
+ enum
+ {
+#if defined HUFFMAN_BITS_2 /* HuffmanBits parallel huffman tables */
+ BITS_PER_LEVEL = 2,
+ ENTRIES_PER_LEVEL = 4
+#elif defined HUFFMAN_BITS_3
+ BITS_PER_LEVEL = 3,
+ ENTRIES_PER_LEVEL = 8
+#elif defined HUFFMAN_BITS_4 /* HuffmanBits parallel huffman tables */
+ BITS_PER_LEVEL = 4,
+ ENTRIES_PER_LEVEL = 16
+#endif
+
+ };
+
+protected:
+
+private:
+
+ typedef struct
+ {
+ unsigned int linbits;
+ const unsigned short(*table)[ENTRIES_PER_LEVEL];
+ } huffmantab;
+
+ static const huffmantab ht[];
+
+ unsigned int nTableIndex;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/l3reg.h b/Src/replicant/nsmp3dec/l3reg.h
new file mode 100644
index 000000000..dcabdfa46
--- /dev/null
+++ b/Src/replicant/nsmp3dec/l3reg.h
@@ -0,0 +1,112 @@
+/***************************************************************************\
+ *
+ * (C) copyright Fraunhofer - IIS (1996)
+ * All Rights Reserved
+ *
+ * filename: l3reg.h
+ * project : <none>
+ * author : Martin Sieler
+ * date : 1996-11-05
+ * contents/description: HEADER - registered types for MPEG Layer-3
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:02 $
+ * $Header: /cvs/root/nullsoft/Replicant/jni/nsmp3/l3reg.h,v 1.1 2010/11/17 20:46:02 audiodsp Exp $
+ */
+
+#ifndef __L3REG_H__
+#define __L3REG_H__
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+#ifdef _MSC_VER
+ #pragma pack(push, 1) /* assume byte packing throughout */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+//==========================================================================;
+//
+// ISO/MPEG Layer3 Format Tag
+//
+#define WAVE_FORMAT_MPEGLAYER3 0x0055
+
+//==========================================================================;
+//
+// Manufacturer ID and Product ID
+//
+#define MM_FRAUNHOFER_IIS 172
+#define MM_FHGIIS_MPEGLAYER3 10
+
+#define MM_FHGIIS_MPEGLAYER3_DECODE 9
+#define MM_FHGIIS_MPEGLAYER3_LITE 10
+#define MM_FHGIIS_MPEGLAYER3_BASIC 11
+#define MM_FHGIIS_MPEGLAYER3_ADVANCED 12
+#define MM_FHGIIS_MPEGLAYER3_PROFESSIONAL 13
+
+#define MM_FHGIIS_MPEGLAYER3_ADVANCEDPLUS 14
+
+//==========================================================================;
+//
+//
+//
+//==========================================================================;
+
+#ifdef MPEGLAYER3_WFX_EXTRA_BYTES
+ //
+ // seems like the structure below is already defined
+ //
+#else
+
+//==========================================================================;
+//
+// MPEG Layer3 WAVEFORMATEX structure
+//
+#define MPEGLAYER3_WFX_EXTRA_BYTES 12
+
+// WAVE_FORMAT_MPEGLAYER3 format structure
+//
+typedef struct tagMPEGLAYER3WAVEFORMAT
+ {
+ WAVEFORMATEX wfx;
+
+ WORD wID;
+ DWORD fdwFlags;
+ WORD nBlockSize;
+ WORD nFramesPerBlock;
+ WORD nCodecDelay;
+
+ } MPEGLAYER3WAVEFORMAT;
+
+typedef MPEGLAYER3WAVEFORMAT * PMPEGLAYER3WAVEFORMAT;
+typedef MPEGLAYER3WAVEFORMAT NEAR *NPMPEGLAYER3WAVEFORMAT;
+typedef MPEGLAYER3WAVEFORMAT FAR *LPMPEGLAYER3WAVEFORMAT;
+
+#endif
+
+//==========================================================================;
+
+#define MPEGLAYER3_ID_UNKNOWN 0
+#define MPEGLAYER3_ID_MPEG 1
+#define MPEGLAYER3_ID_CONSTANTFRAMESIZE 2
+
+#define MPEGLAYER3_FLAG_PADDING_ISO 0x00000000
+#define MPEGLAYER3_FLAG_PADDING_ON 0x00000001
+#define MPEGLAYER3_FLAG_PADDING_OFF 0x00000002
+
+#define MPEGLAYER3_FLAG_CRC_ON 0x00000010
+#define MPEGLAYER3_FLAG_CRC_OFF 0x00000020
+
+#define MPEGLAYER3_FLAG_VBR 0x00000100
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef _MSC_VER
+ #pragma pack(pop) /* revert to previous packing */
+#endif
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/l3table.h b/Src/replicant/nsmp3dec/l3table.h
new file mode 100644
index 000000000..b50f88722
--- /dev/null
+++ b/Src/replicant/nsmp3dec/l3table.h
@@ -0,0 +1,47 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* � 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: l3table.h
+ * project : ISO/MPEG-Decoder
+ * author : Martin Sieler
+ * date : 1998-05-26
+ * contents/description: HEADER - tables for iso/mpeg-decoding (layer3)
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:02 $
+ * $Id: l3table.h,v 1.1 2010/11/17 20:46:02 audiodsp Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef __L3TABLE_H__
+#define __L3TABLE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* ------------------------ includes --------------------------------------*/
+
+/* ------------------------------------------------------------------------*/
+
+ struct SF_BAND_INDEX
+{
+ int l[23];
+ int s[14];
+};
+
+/* ------------------------------------------------------------------------*/
+
+extern const SF_BAND_INDEX sfBandIndex[3][3];
+
+/*-------------------------------------------------------------------------*/
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/Src/replicant/nsmp3dec/mdct.h b/Src/replicant/nsmp3dec/mdct.h
new file mode 100644
index 000000000..0c5073401
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mdct.h
@@ -0,0 +1,61 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mdct.h
+ * project : ISO/MPEG-Decoder
+ * author : Stefan Gewinner
+ * date : 1998-05-26
+ * contents/description: mdct class - HEADER
+ *
+ *
+ \***************************************************************************/
+
+/*
+ * $Date: 2011/01/18 18:22:03 $
+ * $Id: mdct.h,v 1.4 2011/01/18 18:22:03 audiodsp Exp $
+ */
+
+#ifndef __MDCT_H__
+#define __MDCT_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+#include "foundation/align.h"
+/*-------------------------------------------------------------------------*/
+
+//
+// MDCT class.
+//
+// This object performs the frequency-to-time mapping.
+//
+
+class CMdct
+{
+
+public :
+
+ CMdct(const MPEG_INFO &_info);
+ ~CMdct() {}
+
+ void Init();
+ void Apply(int ch, const MP3SI_GRCH &SiGrCH, SPECTRUM &rs);
+
+protected :
+
+ void cos_t_h_long (float *prev,float *dest,const float *win);
+ void cos_t_h_short(float *prev,float *dest,const float *win);
+
+ float hybrid_res[36];
+ NALIGN(16) float cost36_rese[9];
+ NALIGN(16) float cost36_reso[9];
+
+ const MPEG_INFO &info;
+ SPECTRUM prevblck;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/meanvalue.h b/Src/replicant/nsmp3dec/meanvalue.h
new file mode 100644
index 000000000..5f1a3e587
--- /dev/null
+++ b/Src/replicant/nsmp3dec/meanvalue.h
@@ -0,0 +1,61 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: meanvalue.h
+ * project : ---
+ * author : Martin Sieler
+ * date : 1998-02-14
+ * contents/description: HEADER - calc mean value
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:03 $
+ * $Id: meanvalue.h,v 1.1 2010/11/17 20:46:03 audiodsp Exp $
+ */
+
+#ifndef __MEANVALUE_H__
+#define __MEANVALUE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+class CMeanValue
+{
+public:
+
+ CMeanValue() { Reset(); }
+
+ void Reset();
+ CMeanValue& operator+= (int nValue);
+
+ operator int() const { return m_Count ? m_Sum/m_Count : 0; }
+ operator float() const { return m_Count ? float(m_Sum)/float(m_Count) : 0.0f; }
+
+ int GetSum() const { return m_Sum; }
+ int GetCount() const { return m_Count; }
+ int GetMin() const { return m_Min; }
+ int GetMax() const { return m_Max; }
+ bool IsFixed() const { return m_bFixed; }
+
+protected:
+
+private:
+
+ int m_Count;
+ int m_Sum;
+ int m_FirstValue;
+ int m_Min;
+ int m_Max;
+ bool m_bFixed;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mp2decode.h b/Src/replicant/nsmp3dec/mp2decode.h
new file mode 100644
index 000000000..d918330b2
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp2decode.h
@@ -0,0 +1,50 @@
+#ifndef __MP2DECODE_H__
+#define __MP2DECODE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+#include "mpegbitstream.h"
+#include "polyphase.h"
+
+/*-------------------------------------------------------------------------*/
+
+//
+// MPEG Layer-2 decoding class.
+//
+// This is the main MPEG Layer-2 decoder object.
+//
+
+class CMp2Decode
+{
+public:
+ CMp2Decode(CMpegBitStream &_Bs, DecoderHooks *_hooks=0);
+ ~CMp2Decode();
+
+ void Init(bool fFullReset = true);
+
+ SSC Decode(void *pPcm, size_t cbPcm, size_t *pcbUsed);
+
+private:
+ SSC Decode2(void *pPcm);
+ SSC Decode1(void *pPcm);
+
+ void ZeroPolySpectrum();
+ void SetInfo();
+
+ CPolyphase m_Polyphase; // polyphase
+
+ MPEG_INFO m_Info; // info structure
+ CMpegBitStream &m_Bs; // bitstream
+
+ POLYSPECTRUM m_PolySpectrum; // spectrum (post-mdct)
+
+ char m_tab_3[32 * 3];
+ char m_tab_5[128 * 3];
+ char m_tab_9[1024 * 3];
+ float m_scales[27][64];
+ DecoderHooks *hooks;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3ancofl.h b/Src/replicant/nsmp3dec/mp3ancofl.h
new file mode 100644
index 000000000..77e1ea013
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3ancofl.h
@@ -0,0 +1,86 @@
+/***************************************************************************\
+*
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+* All Rights Reserved
+*
+* filename: mp3ancofl.h
+* project : MPEG Decoder
+* author : Dieter Weninger
+* date : 2003-05-14
+* contents: ancillary data and original file length - HEADER
+*
+\***************************************************************************/
+#ifndef __MP3ANCOFL_H__
+#define __MP3ANCOFL_H__
+
+#include "mpegbitstream.h"
+
+#define ID_OFL 0xB
+#define VERSION_0_LEN 8 /* bytes */
+#define VERSION_1_LEN 10 /* bytes */
+
+class CMp3AncOfl
+{
+ public:
+ CMp3AncOfl(CBitStream &__Db);
+ ~CMp3AncOfl();
+
+ void Reset(void);
+
+ int getVersion(void);
+ unsigned int getTotalLength(void);
+ unsigned int getCodecDelay(void);
+ unsigned int getAddDelay(void);
+
+ bool validOfl(void);
+
+ void fetchOfl(int oflOn,
+ CBitStream &Db,
+ int beforeScf,
+ unsigned int* startDelay,
+ unsigned int* totalLength);
+
+ int readAnc(unsigned char *ancBytes,
+ CBitStream &Db,
+ const int numAncBits);
+
+ int doReadBytes(){return m_readBytes;}
+
+ private:
+ void crcOfl(unsigned short crcPoly,
+ unsigned short crcMask,
+ unsigned long *crc,
+ unsigned char byte);
+
+ void cleanUp(void);
+ bool isFhGAnc( int size);
+ bool readOfl(CBitStream &Db, int beforeScaleFactors);
+ bool isOfl(void);
+ bool justSearched(void);
+ int toSkip(void);
+ void getOfl(CBitStream &Db, const int len);
+
+ CBitStream &m_Db; // dynamic buffer
+
+ unsigned char oflArray[10];
+
+ bool m_valid;
+ bool m_searched;
+ bool m_semaphor;
+ bool m_FhGAncChecked;
+ bool m_collecting;
+ bool m_mp3pro;
+
+ unsigned char* m_FhGAncBuf;
+ unsigned char* m_tmpAncBuf;
+
+ int m_pFhGAncBuf;
+ int m_FhGAncBufSize;
+
+ // flag signalling byte- or bit-wise reading
+ int m_readBytes;
+
+};
+
+#endif /* __MP3ANCOFL_H__ */
diff --git a/Src/replicant/nsmp3dec/mp3decode.h b/Src/replicant/nsmp3dec/mp3decode.h
new file mode 100644
index 000000000..fb9a8048b
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3decode.h
@@ -0,0 +1,113 @@
+/***************************************************************************\
+*
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+* All Rights Reserved
+*
+* filename: mp3decode.h
+* project : ISO/MPEG-Decoder
+* author : Martin Sieler
+* date : 1998-05-26
+* contents/description: MPEG Layer-3 decoder
+*
+*
+\***************************************************************************/
+
+/*
+* $Date: 2011/01/28 21:45:29 $
+* $Id: mp3decode.h,v 1.5 2011/01/28 21:45:29 audiodsp Exp $
+*/
+
+#ifndef __MP3DECODE_H__
+#define __MP3DECODE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+#include "mpegbitstream.h"
+#include "huffdec.h"
+#include "mdct.h"
+#include "polyphase.h"
+#include "mp3ancofl.h"
+
+#ifdef ERROR_CONCEALMENT
+#include "conceal.h"
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+//
+// MPEG Layer-3 decoding class.
+//
+// This is the main MPEG Layer-3 decoder object.
+//
+
+class NALIGN(16) CMp3Decode
+{
+public:
+
+ CMp3Decode(CMpegBitStream &_Bs, int _crc_check, DecoderHooks *_hooks=0);
+
+ ~CMp3Decode();
+
+ void Init(bool fFullReset = true);
+
+ // PcmFormat: 0: integer, 1: 32 bit float (IEEE)
+ SSC Decode(float *pPcm,
+ size_t cbPcm,
+ size_t *pcbUsed,
+ unsigned char *ancData,
+ size_t *numAncBytes = 0,
+ int oflOn = 0,
+ unsigned int *startDelay = 0,
+ unsigned int *totalLength = 0);
+
+
+ SSC GetLastAncData(unsigned char* ancData, size_t *numAncBytes);
+
+ SSC GetOflVersion(int* oflVersion);
+
+protected:
+
+ SSC DecodeOnNoMainData(float *pPcm);
+ SSC DecodeNormal (float *pPcm, bool fCrcOk);
+
+ void PolyphaseReorder();
+ void ZeroISpectrum();
+ void ZeroSpectrum();
+ void ZeroPolySpectrum();
+ void SetInfo();
+
+ CMp3Huffman m_Mp3Huffman; // huffman decoder
+ CMdct m_Mdct; // mdct
+ CPolyphase m_Polyphase; // polyphase
+ CMp3AncOfl m_AncOfl; // ancillary data and ofl
+
+#ifdef ERROR_CONCEALMENT
+ CErrorConcealment m_Conceal; // error concealment
+#endif
+
+ MPEG_INFO m_Info; // info structure
+ CMpegBitStream &m_Bs; // bitstream
+ CBitStream m_Db; // dynamic buffer
+ MP3SI m_Si; // side info
+ MP3SCF m_ScaleFac[2]; // scalefactors
+
+ int m_ISpectrum[2][SSLIMIT*SBLIMIT]; // spectrum (integer)
+ NALIGN(16) SPECTRUM m_Spectrum; // spectrum (float)
+ NALIGN(16) POLYSPECTRUM m_PolySpectrum; // spectrum (post-mdct)
+
+ int m_crc_check; // 0: no CRC check, 1: fail on CRC errors
+
+protected:
+
+ enum { dynBufSize = 2048 } ;
+
+ unsigned char m_dynBufMemory [dynBufSize] ;
+
+private:
+ DecoderHooks *hooks;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3drmifc.h b/Src/replicant/nsmp3dec/mp3drmifc.h
new file mode 100644
index 000000000..822e174b2
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3drmifc.h
@@ -0,0 +1,25 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp3drmifc.h
+ * project : MPEG Decoder
+ * author :
+ * date : 2004-12-06
+ * contents/description: DRM Interface
+ *
+ *
+\***************************************************************************/
+
+#ifndef __MP3DRMIFC_H__
+#define __MP3DRMIFC_H__
+
+#include "mp3sscdef.h"
+
+SSC MP3DECAPI mp3decGetScfBuffer(MP3DEC_HANDLE handle,
+ const unsigned char** ppBuffer,
+ unsigned int* pBufSize);
+
+#endif /* __MP3DRMIFC_H__ */
diff --git a/Src/replicant/nsmp3dec/mp3quant.h b/Src/replicant/nsmp3dec/mp3quant.h
new file mode 100644
index 000000000..6304fd598
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3quant.h
@@ -0,0 +1,43 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp_quant.h
+ * project : ISO/MPEG-Decoder
+ * author : Markus Werner, addings: Martin Sieler
+ * date : 1995-07-07
+ * contents/description: HEADER - sample-dequantization
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mp3quant.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef __MP3QUANT_H__
+#define __MP3QUANT_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+
+/* ------------------------------------------------------------------------*/
+
+void mp3DequantizeSpectrum
+ (
+ int *pIData,
+ float *pFData,
+ const MP3SI_GRCH &SiGrCh,
+ const MP3SCF &ScaleFac,
+ const MPEG_INFO &Info
+ );
+
+/*-------------------------------------------------------------------------*/
+
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3read.h b/Src/replicant/nsmp3dec/mp3read.h
new file mode 100644
index 000000000..fcd2ecd92
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3read.h
@@ -0,0 +1,60 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp3read.h
+ * project : ISO/MPEG-Decoder
+ * author : Martin Sieler
+ * date : 1998-05-26
+ * contents/description: mp3 read-functions: sideinfo, main data,
+ * scalefactors
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mp3read.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef __MP3READ_H__
+#define __MP3READ_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+
+/* ------------------------------------------------------------------------*/
+
+class CBitStream;
+
+/* ------------------------------------------------------------------------*/
+
+bool mp3SideInfoRead(CBitStream &Bs, MP3SI &Si, const MPEG_INFO &Info, int crc_check);
+
+bool mp3MainDataRead
+ (
+ CBitStream &Bs, // bitstream
+ CBitStream &Db, // dynamic buffer
+ const MP3SI &Si,
+ const MPEG_INFO &Info
+ );
+
+void mp3ScaleFactorRead
+ (
+ CBitStream &Bs,
+ MP3SI_GRCH &SiGrCh,
+ MP3SCF &ScaleFac,
+ const MPEG_INFO &Info,
+ const int *pScfsi,
+ int gr,
+ int ch
+ );
+
+/*-------------------------------------------------------------------------*/
+
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3ssc.h b/Src/replicant/nsmp3dec/mp3ssc.h
new file mode 100644
index 000000000..ac2876c3c
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3ssc.h
@@ -0,0 +1,57 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp3ssc.h
+ * project : ---
+ * author : Martin Sieler
+ * date : 1999-02-15
+ * contents/description: ssc helper class (Structured Status Code)
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mp3ssc.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+#ifndef __MP3SSC_H__
+#define __MP3SSC_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mp3sscdef.h"
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+/** Helper class for more information about SSC codes.
+*/
+class CMp3Ssc
+{
+public:
+ /** Object constructor
+
+ @param An SSC staus code to initialize the object with.
+
+ */
+ CMp3Ssc(SSC ssc);
+ ~CMp3Ssc() {}
+
+ /** Operator for conversion to a text string.
+
+ @return Textual description.
+
+ */
+ operator const char*();
+
+private:
+ SSC m_ssc;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3sscdef.h b/Src/replicant/nsmp3dec/mp3sscdef.h
new file mode 100644
index 000000000..04432a300
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3sscdef.h
@@ -0,0 +1,154 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp3sscdef.h
+ * project : ---
+ * author : Martin Sieler
+ * date : 1998-02-16
+ * contents/description: ssc definitions (Structured Status Code)
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mp3sscdef.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+#ifndef __MP3SSCDEF_H__
+#define __MP3SSCDEF_H__
+
+/*------------------------- includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*\
+ *
+ * Standard error/return values are 32 bit values layed out as follows:
+ *
+ * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +---+-+-+-----------------------+-------------------------------+
+ * |Sev|C|R| Handler | Code |
+ * +---+-+-+-----------------------+-------------------------------+
+ *
+ * where
+ *
+ * Sev - is the severity code
+ *
+ * 00 - Success
+ * 01 - Informational
+ * 10 - Warning
+ * 11 - Error
+ *
+ * C - is the Customer code flag
+ *
+ * R - is a reserved bit
+ *
+ * Handler - is the handler code
+ *
+ * Code - is the facility's status code
+ *
+\*-------------------------------------------------------------------------*/
+
+/*
+ * define the Severity codes
+ */
+
+#define SSC_SEV_SUCCESS 0x00000000L
+#define SSC_SEV_INFO 0x40000000L
+#define SSC_SEV_WARNING 0x80000000L
+#define SSC_SEV_ERROR 0xc0000000L
+
+/*
+ * define masks to extract the fields
+ */
+
+#define SSC_MASK_SEVERITY 0xc0000000L
+#define SSC_MASK_HANDLER 0x0fff0000L
+#define SSC_MASK_CODE 0x0000ffffL
+
+/*
+ * define MACROS to test an error/return code
+ */
+
+#define SSC_GETSEV(x) ( (x) & SSC_MASK_SEVERITY )
+
+
+/* Check, if an SSC indicates success */
+#define SSC_SUCCESS(x) (((SSC_GETSEV(x)==SSC_SEV_SUCCESS)||(SSC_GETSEV(x)==SSC_SEV_INFO))?1:0)
+
+/* Check, if an SSC indicates an information */
+#define SSC_INFO(x) ((SSC_GETSEV(x)==SSC_SEV_INFO)?1:0)
+
+/* Check, if an SSC indicates a warning */
+#define SSC_WARNING(x) ((SSC_GETSEV(x)==SSC_SEV_WARNING)?1:0)
+
+/* Check, if an SSC indicates an error */
+#define SSC_ERROR(x) ((SSC_GETSEV(x)==SSC_SEV_ERROR)?1:0)
+
+/*-------------------------------------------------------------------------*\
+ *
+ * SSC classes (handler)
+ *
+\*-------------------------------------------------------------------------*/
+
+#define SSC_HANDLER_GEN 0x00000000L
+
+#define SSC_I_GEN (SSC_SEV_INFO | SSC_HANDLER_GEN)
+#define SSC_W_GEN (SSC_SEV_WARNING | SSC_HANDLER_GEN)
+#define SSC_E_GEN (SSC_SEV_ERROR | SSC_HANDLER_GEN)
+
+/*-------------------------------------------------------------------------*/
+
+#define SSC_HANDLER_IO 0x00010000L
+
+#define SSC_I_IO (SSC_SEV_INFO | SSC_HANDLER_IO)
+#define SSC_W_IO (SSC_SEV_WARNING | SSC_HANDLER_IO)
+#define SSC_E_IO (SSC_SEV_ERROR | SSC_HANDLER_IO)
+
+/*-------------------------------------------------------------------------*/
+
+#define SSC_HANDLER_MPGA 0x01010000L
+
+#define SSC_I_MPGA (SSC_SEV_INFO | SSC_HANDLER_MPGA)
+#define SSC_W_MPGA (SSC_SEV_WARNING | SSC_HANDLER_MPGA)
+#define SSC_E_MPGA (SSC_SEV_ERROR | SSC_HANDLER_MPGA)
+
+/*-------------------------------------------------------------------------*\
+ *
+ * SSC codes
+ *
+\*-------------------------------------------------------------------------*/
+
+typedef enum
+ {
+ SSC_OK = 0x00000000L,
+
+ SSC_E_WRONGPARAMETER = (SSC_E_GEN | 1),
+ SSC_E_OUTOFMEMORY = (SSC_E_GEN | 2),
+ SSC_E_INVALIDHANDLE = (SSC_E_GEN | 3),
+
+ SSC_E_IO_GENERIC = (SSC_W_IO | 1),
+ SSC_E_IO_OPENFAILED = (SSC_W_IO | 2),
+ SSC_E_IO_CLOSEFAILED = (SSC_W_IO | 3),
+ SSC_E_IO_READFAILED = (SSC_W_IO | 4),
+
+ SSC_I_MPGA_CRCERROR = (SSC_I_MPGA | 1),
+ SSC_I_MPGA_NOMAINDATA = (SSC_I_MPGA | 2),
+
+ SSC_E_MPGA_GENERIC = (SSC_E_MPGA | 1),
+ SSC_E_MPGA_WRONGLAYER = (SSC_E_MPGA | 2),
+ SSC_E_MPGA_BUFFERTOOSMALL = (SSC_E_MPGA | 3),
+
+ SSC_W_MPGA_SYNCSEARCHED = (SSC_W_MPGA | 1),
+ SSC_W_MPGA_SYNCLOST = (SSC_W_MPGA | 2),
+ SSC_W_MPGA_SYNCNEEDDATA = (SSC_W_MPGA | 3),
+ SSC_W_MPGA_SYNCEOF = (SSC_W_MPGA | 4)
+ } SSC;
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3streaminfo.h b/Src/replicant/nsmp3dec/mp3streaminfo.h
new file mode 100644
index 000000000..7c1eefda9
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3streaminfo.h
@@ -0,0 +1,117 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp3streaminfo.h
+ * project : MPEG Layer-3 Decoder
+ * author : Martin Sieler
+ * date : 1998-05-27
+ * contents/description: current bitstream parameters
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mp3streaminfo.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+#ifndef __MP3STREAMINFO_H__
+#define __MP3STREAMINFO_H__
+
+/* ------------------------ structure alignment ---------------------------*/
+
+#ifdef WIN32
+ #pragma pack(push, 8)
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+typedef struct
+{
+ int m_Layer; /* ISO/MPEG Layer */
+ int m_MpegVersion; /* ISO/MPEG Version */
+ int m_Bitrate; /* Bitrate (Bit/s) */
+ int m_BitrateIndex; /* ISO/MPEG Bitrate index of frame */
+ int m_Channels; /* Number of Channels (as indicated) */
+ int m_SFreq; /* Sampling Frequency (as indicated) */
+ int m_EffectiveChannels; /* Number of effective output channels */
+ int m_EffectiveSFreq; /* Effective Sampling Frequency */
+ int m_BitsPerFrame; /* Number of bits in frame */
+ float m_Duration; /* Duration of frame in milli seconds */
+ int m_CrcError; /* Indication of CRC Errors */
+ int m_NoMainData; /* Indication of missing main data */
+ int m_SamplesPerFrame;
+ } MP3STREAMINFO;
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+
+//
+// Mp3 Streaminfo object.
+//
+// Object holding information on the last successfully decode frame.
+//
+
+class CMp3StreamInfo : protected MP3STREAMINFO
+{
+public:
+
+ CMp3StreamInfo() { Reset(); }
+
+ int GetLayer() const { return m_Layer; }
+ int GetMpegVersion() const { return m_MpegVersion; }
+ int GetBitrate() const { return m_Bitrate; }
+ int GetBitrateIndex() const { return m_BitrateIndex; }
+ int GetChannels() const { return m_Channels; }
+ int GetSFreq() const { return m_SFreq; }
+ int GetBitsPerFrame() const { return m_BitsPerFrame; }
+ float GetDuration() const { return m_Duration; }
+ int GetCrcError() const { return m_CrcError; }
+ int GetNoMainData() const { return m_NoMainData; }
+ int GetSamplesPerFrame() const { return m_SamplesPerFrame; }
+
+protected:
+
+ friend class CMpgaDecoder;
+
+ void SetLayer(int nValue) { m_Layer = nValue; }
+ void SetMpegVersion(int nValue) { m_MpegVersion = nValue; }
+ void SetBitrate(int nValue) { m_Bitrate = nValue; }
+ void SetBitrateIndex(int nValue) { m_BitrateIndex = nValue; }
+ void SetChannels(int nValue) { m_Channels = nValue; }
+ void SetSFreq(int nValue) { m_SFreq = nValue; }
+ void SetBitsPerFrame(int nValue) { m_BitsPerFrame = nValue; }
+ void SetDuration(float fValue) { m_Duration = fValue; }
+ void SetCrcError(int nValue) { m_CrcError = nValue; }
+ void SetNoMainData(int nValue) { m_NoMainData = nValue; }
+ void SetSamplesPerFrame(int nValue) { m_SamplesPerFrame = nValue; }
+
+ void Reset()
+ {
+ m_Layer = 0;
+ m_MpegVersion = 0;
+ m_Bitrate = 0;
+ m_BitrateIndex = 0;
+ m_Channels = 0;
+ m_SFreq = 0;
+ m_BitsPerFrame = 0;
+ m_Duration = 0.0f;
+ m_CrcError = 0;
+ m_NoMainData = 0;
+ m_SamplesPerFrame=0;
+ }
+};
+
+#endif /* __cplusplus */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef WIN32
+ #pragma pack(pop)
+#endif
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mp3tools.h b/Src/replicant/nsmp3dec/mp3tools.h
new file mode 100644
index 000000000..229e38570
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mp3tools.h
@@ -0,0 +1,66 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mp3tools.h
+ * project : ISO/MPEG-Decoder
+ * author : Martin Sieler
+ * date : 1998-05-26
+ * contents/description: HEADER - layer III processing
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/13 22:43:21 $
+ * $Id: mp3tools.h,v 1.2 2011/01/13 22:43:21 audiodsp Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef __MP3TOOLS_H__
+#define __MP3TOOLS_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+
+/* ------------------------------------------------------------------------*/
+
+void mp3ScaleFactorUpdate
+ (
+ const MP3SI_GRCH &SiL,
+ const MP3SI_GRCH &SiR,
+ const MPEG_INFO &Info,
+ MP3SCF &ScaleFac
+ );
+
+void mp3StereoProcessing
+ (
+ float *pLeft,
+ float *pRight,
+ MP3SI_GRCH &SiL,
+ MP3SI_GRCH &SiR,
+ const MP3SCF &ScaleFac, /* right channel!! */
+ const MPEG_INFO &Info
+ );
+
+void mp3Reorder
+ (
+ float *pData,
+ const MP3SI_GRCH &Si,
+ const MPEG_INFO &Info
+ );
+
+void mp3Antialias
+ (
+ float *pData,
+ MP3SI_GRCH &Si,
+ const MPEG_INFO &Info
+ );
+
+/*-------------------------------------------------------------------------*/
+
+#endif
diff --git a/Src/replicant/nsmp3dec/mpeg.h b/Src/replicant/nsmp3dec/mpeg.h
new file mode 100644
index 000000000..00f06e8bd
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mpeg.h
@@ -0,0 +1,174 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mpeg.h
+ * project : ISO/MPEG-Decoder
+ * author : Markus Werner, addings: Martin Sieler
+ * date : 1995-07-07
+ * contents/description: HEADER - iso/mpeg-definitions
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/20 22:14:40 $
+ * $Id: mpeg.h,v 1.3 2011/01/20 22:14:40 audiodsp Exp $
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef __MPEG_H__
+#define __MPEG_H__
+#include "foundation/align.h"
+
+/* ------------------------ includes --------------------------------------*/
+
+/* ------------------------------------------------------------------------*/
+
+//
+// MPEG ID (fhgVersion)
+//
+#define MPG_MPEG1 1
+#define MPG_MPEG2 0
+#define MPG_MPEG25 2
+
+/* ------------------------------------------------------------------------*/
+
+//
+// sample rate
+//
+#define MPG_SF_LOW 2
+
+/* ------------------------------------------------------------------------*/
+
+//
+// header-mode field
+//
+#define MPG_MD_STEREO 0
+#define MPG_MD_JOINT_STEREO 1
+#define MPG_MD_DUAL_CHANNEL 2
+#define MPG_MD_MONO 3
+
+/*-------------------------------------------------------------------------*/
+
+//
+// channels
+//
+#define MONO 1
+#define STEREO 2
+
+/* ------------------------------------------------------------------------*/
+
+//
+// subbands, samples/subband
+//
+#define SBLIMIT 32
+#define SSLIMIT 18
+
+/* ------------------------------------------------------------------------*/
+
+//
+// info structure
+//
+typedef struct
+ {
+ int stereo;
+ int sample_rate_ndx;
+ int frame_bits;
+ int mode;
+ int mode_ext;
+ int header_size;
+ int fhgVersion;
+ int protection;
+ bool IsMpeg1;
+ } MPEG_INFO;
+
+/* ------------------------------------------------------------------------*/
+
+//
+// MPEG Layer-3 sideinfo (per channel/granule)
+//
+typedef struct
+ {
+ int part2_3_length;
+ int big_values;
+ int global_gain;
+ int scalefac_compress;
+ int window_switching_flag;
+ int block_type;
+ int mixed_block_flag;
+ int table_select[3];
+ int subblock_gain[3];
+ int region0_count;
+ int region1_count;
+ int preflag;
+ int scalefac_scale;
+ int count1table_select;
+
+ // additional calced values
+ int intensity_scale; // MPEG 2, MPEG 2.5 only
+ int zeroStartNdx;
+ int zeroSfbStartNdxIsLong;
+ int zeroSfbStartNdxL;
+ int zeroSfbStartNdxSMax;
+ int zeroSfbStartNdxS[3];
+ int zeroSbStartNdx;
+ } MP3SI_GRCH;
+
+/* ------------------------------------------------------------------------*/
+
+//
+// MPEG Layer-3 sideinfo
+//
+typedef struct
+ {
+ int main_data_begin;
+ int private_bits;
+ struct
+ {
+ int scfsi[4];
+ MP3SI_GRCH gr[2];
+ } ch[2];
+ } MP3SI;
+
+/* ------------------------------------------------------------------------*/
+
+//
+// MPEG Layer-3 scalefactors
+//
+typedef struct
+ {
+ // scalefactors
+ int l[23];
+ int s[3][13];
+
+ // illegal intensity position
+ int l_iip[23];
+ int s_iip[13];
+ } MP3SCF;
+
+/* ------------------------------------------------------------------------*/
+
+//
+// spectrum (as transmitted)
+//
+typedef NALIGN(16) float SPECTRUM[2][SBLIMIT][SSLIMIT];
+
+//
+// spectrum (after mdct)
+//
+typedef NALIGN(16) float POLYSPECTRUM[2][SSLIMIT][SBLIMIT];
+
+/* Nullsoft added 25 Oct 2007 */
+struct DecoderHooks
+{
+ void (*layer3_vis)(SPECTRUM vistable,int gr, int nch);
+ void (*layer2_eq)(float *xr, int nch, int srate, int nparts);
+ void (*layer3_eq)(float *xr, int nch, int srate);
+};
+
+/* ------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mpegbitstream.h b/Src/replicant/nsmp3dec/mpegbitstream.h
new file mode 100644
index 000000000..ec37626a8
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mpegbitstream.h
@@ -0,0 +1,71 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mpegbitstream.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1997-12-05
+ * contents/description: MPEG bitstream - HEADER
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mpegbitstream.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+#ifndef __MPEGBITSTREAM_H__
+#define __MPEGBITSTREAM_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "bitstream.h"
+#include "mpegheader.h"
+#include "mp3sscdef.h"
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+//
+// MPEG bitstream class.
+//
+// This object is derived from CBitStream. In addition to CBitStream
+// this object is able to sync to the next ISO/MPEG header position.
+//
+
+class CMpegBitStream : public CBitStream
+{
+public:
+ CMpegBitStream(int cbSize);
+ CMpegBitStream(unsigned char *pBuf, int cbSize, bool fDataValid = false);
+ virtual ~CMpegBitStream();
+
+ virtual void Reset();
+
+ SSC DoSync();
+ int GetSyncPosition() const { return m_SyncPosition; }
+ const CMpegHeader *GetHdr() const { return &m_Hdr; }
+
+protected:
+
+private:
+
+ SSC DoSyncInitial();
+ SSC DoSyncContinue();
+
+ enum { FRAMES_TO_CHECK = 10 };
+
+ CMpegHeader m_Hdr; // mpeg header
+ unsigned long m_FirstHdr; // "relevant" bits of first good header
+ unsigned long m_nFramesToCheck; // # frames to be checked for next mpeg header
+ int m_SyncPosition; // offset of first sync in bits
+ SSC m_SyncState; // last sync state
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mpegheader.h b/Src/replicant/nsmp3dec/mpegheader.h
new file mode 100644
index 000000000..0afb91381
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mpegheader.h
@@ -0,0 +1,105 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mpegheader.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1997-12-05
+ * contents/description: ISO/MPEG Header
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:04 $
+ * $Id: mpegheader.h,v 1.1 2010/11/17 20:46:04 audiodsp Exp $
+ */
+
+#ifndef __MPEGHEADER_H__
+#define __MPEGHEADER_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+class CBitStream;
+
+/*-------------------------------------------------------------------------*/
+
+//
+// MPEG header class.
+//
+// This object reads and decodes an ISO/MPEG header.
+//
+
+class CMpegHeader
+{
+public:
+ CMpegHeader();
+ virtual ~CMpegHeader();
+
+ int ReadFrom(CBitStream &sBS);
+ int FromInt(unsigned long dwHdrBits);
+
+ int GetMpegVersion() const { return m_MpegVersion;}
+ int GetLayer() const { return m_Layer;}
+ int GetChannels() const { return m_Channels;}
+ int GetSampleRate() const { return m_SampleRate;}
+ int GetSampleRateNdx() const { return m_SampleRateNdx;}
+ int GetBitrate() const { return m_Bitrate;}
+ int GetBitrateNdx() const { return m_BitrateNdx;}
+ int GetMode() const { return m_Mode;}
+ int GetModeExt() const { return m_ModeExt;}
+ int GetPadding() const { return m_Padding; }
+ int GetCrcCheck() const { return m_CrcCheck;}
+ int GetCopyright() const { return m_Copyright;}
+ int GetOriginal() const { return m_Original;}
+ int GetEmphasis() const { return m_Emphasis;}
+
+ int GetHeaderLen() const
+ { return MPEG_HDRLEN+(m_CrcCheck?MPEG_CRCLEN:0); }
+ int GetFrameLen() const { return m_FrameLen;}
+ float GetDuration() const { return m_Duration;}
+ int GetSamplesPerFrame() const;
+
+protected:
+
+private:
+
+ enum { MPEG_HDRLEN = 32, MPEG_CRCLEN = 16 };
+
+ int CalcFrameLen();
+ void ResetMembers();
+ void SetMembers();
+
+ // header fields
+ int m_Syncword;
+ int m_Idex;
+ int m_Id;
+ int m_Layer;
+ int m_CrcCheck;
+ int m_BitrateNdx;
+ int m_SampleRateNdx;
+ int m_Padding;
+ int m_Private;
+ int m_Mode;
+ int m_ModeExt;
+ int m_Copyright;
+ int m_Original;
+ int m_Emphasis;
+
+ // calculated data
+ int m_HeaderValid;
+ int m_MpegVersion;
+ int m_Channels;
+ int m_SampleRate;
+ int m_Bitrate;
+ int m_FrameLen;
+ float m_Duration;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/mpgadecoder.h b/Src/replicant/nsmp3dec/mpgadecoder.h
new file mode 100644
index 000000000..3466cf766
--- /dev/null
+++ b/Src/replicant/nsmp3dec/mpgadecoder.h
@@ -0,0 +1,100 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: mpgadecoder.h
+ * project : MPEG Decoder
+ * author : Martin Sieler
+ * date : 1998-05-26
+ * contents/description: MPEG Decoder class - HEADER
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/01/25 18:24:17 $
+ * $Id: mpgadecoder.h,v 1.4 2011/01/25 18:24:17 audiodsp Exp $
+ */
+
+#ifndef __MPGADECODER_H__
+#define __MPGADECODER_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mp3sscdef.h"
+#include "mp3streaminfo.h"
+#include "mpegbitstream.h"
+#include "mp3decode.h"
+#include "mp2decode.h"
+
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+//
+// Mp3 Decoder Top Level Object.
+//
+// This is the main ISO/MPEG decoder object that interfaces with the
+// application code.
+//
+// It is however recommended to use IMpgaDecoder (see mp3decifc.h) instead.
+// Define USE_MP3DECIFC when planning to use IMpgaDecoder.
+//
+
+enum
+{
+ MPEGAUDIO_CRCCHECK_OFF = 0,
+ MPEGAUDIO_CRCCHECK_ON = 1,
+};
+
+class CMpgaDecoder
+{
+public:
+ CMpgaDecoder(int crcCheck = MPEGAUDIO_CRCCHECK_OFF);
+ CMpgaDecoder(DecoderHooks *hooks, int crcCheck = MPEGAUDIO_CRCCHECK_OFF);
+ CMpgaDecoder(unsigned char *pBuf, int cbSize, int crcCheck = MPEGAUDIO_CRCCHECK_OFF);
+ ~CMpgaDecoder();
+ void *operator new(size_t stAllocateBlock);
+ void operator delete(void *);
+
+ void Reset();
+
+ SSC DecodeFrame(float *pPcm, size_t cbPcm, size_t *pcbUsed = 0, unsigned char *ancData = 0, size_t *numAncBytes = 0, int oflOn = 0, unsigned int *startDelay = 0, unsigned int *totalLength = 0);
+
+ const CMp3StreamInfo *GetStreamInfo() const;
+
+ void Connect(CGioBase *gf);
+ int Fill(const unsigned char *pBuffer, int cbBuffer);
+ int GetInputFree() const;
+ int GetInputLeft() const;
+ void SetInputEof();
+ bool IsEof() const;
+
+#ifdef KSA_DRM
+ int GetScfBuffer(const unsigned char** ppBuffer, unsigned int* pBufSize) const;
+#endif
+
+ SSC GetLastAncData(unsigned char* ancData = 0, size_t *numAncBytes = 0);
+
+ SSC GetOflVersion(int* oflVersion = 0);
+//protected:
+
+ void SetStreamInfo(SSC dwReturn);
+
+
+ CMp3StreamInfo m_Info;
+ CMpegBitStream m_Mbs;
+ NALIGN(16) CMp3Decode m_Mp3Decode;
+ NALIGN(16) CMp2Decode m_Mp2Decode;
+ bool m_IsEof;
+ int m_Layer;
+
+private:
+
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nsmp3dec/polyphase.h b/Src/replicant/nsmp3dec/polyphase.h
new file mode 100644
index 000000000..0d221274f
--- /dev/null
+++ b/Src/replicant/nsmp3dec/polyphase.h
@@ -0,0 +1,61 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: polyphase.h
+ * project : ISO/MPEG-Decoder
+ * author : Stefan Gewinner
+ * date : 1998-05-26
+ * contents/description: polyphase class - HEADER
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2011/02/14 14:48:56 $
+ * $Id: polyphase.h,v 1.6 2011/02/14 14:48:56 bigg Exp $
+ */
+
+#pragma once
+//#ifndef __POLYPHASE_H__
+//#define __POLYPHASE_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+#include "mpeg.h"
+#include "foundation/align.h"
+/*-------------------------------------------------------------------------*/
+
+#define HAN_SIZE 512
+
+/*-------------------------------------------------------------------------*/
+
+// Class for (inverse) Polyphase calculation.
+
+class CPolyphase
+{
+
+public:
+
+ CPolyphase(const MPEG_INFO &_info);
+
+ ~CPolyphase() {}
+
+ void Init();
+ float *Apply(POLYSPECTRUM &sample, float *pPcm, int frms=18);
+ static void Reorder(int channels, POLYSPECTRUM &output, const SPECTRUM &input);
+protected:
+
+ int bufOffset;
+ NALIGN(16) float syn_buf[2][HAN_SIZE];
+
+ const MPEG_INFO &info ; // info-structure
+
+ void window_band_m(int bufOffset, float *out_samples) const;
+ void window_band_s(int bufOffset, float *out_samples) const;
+};
+
+/*-------------------------------------------------------------------------*/
+//#endif
diff --git a/Src/replicant/nsmp3dec/precomp.h b/Src/replicant/nsmp3dec/precomp.h
new file mode 100644
index 000000000..c4708f9c7
--- /dev/null
+++ b/Src/replicant/nsmp3dec/precomp.h
@@ -0,0 +1,19 @@
+//
+// precomp.h
+// nsmp3
+//
+
+#include <assert.h>
+#include <iostream>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "foundation/align.h"
+
+#include "bitstream.h"
+#include "mpeg.h"
+#include "mp3ssc.h"
+#include "mp3sscdef.h"
diff --git a/Src/replicant/nsmp3dec/regtypes.h b/Src/replicant/nsmp3dec/regtypes.h
new file mode 100644
index 000000000..5de3c2cf2
--- /dev/null
+++ b/Src/replicant/nsmp3dec/regtypes.h
@@ -0,0 +1,66 @@
+/***************************************************************************\
+ *
+ * (C) copyright Fraunhofer - IIS (1998)
+ * All Rights Reserved
+ *
+ * filename: regtypes.h
+ * project : -
+ * author : Stefan Gewinner gew@iis.fhg.de
+ * date : 1998-06-08
+ * contents/description: absolute minimum to make l3reg.h compile without windef.h
+ *
+ * $Header: /cvs/root/nullsoft/Replicant/jni/nsmp3/regtypes.h,v 1.1 2010/11/17 20:46:05 audiodsp Exp $
+ *
+\***************************************************************************/
+
+/* the typedefs should be in place if we already got windows.h included */
+
+#ifndef _INC_WINDOWS
+
+#ifndef __REGTYPES_H__
+#define __REGTYPES_H__
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef _MSC_VER
+ #pragma warning(disable:4103)
+ #pragma pack(push, 1) /* assume byte packing throughout */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define FAR
+#define NEAR
+
+typedef unsigned long DWORD ;
+typedef unsigned short WORD ;
+
+/*
+ * extended waveform format structure used for all non-PCM formats. this
+ * structure is common to all non-PCM formats.
+ */
+typedef struct tagWAVEFORMATEX
+{
+ WORD wFormatTag; /* format type */
+ WORD nChannels; /* number of channels (i.e. mono, stereo...) */
+ DWORD nSamplesPerSec; /* sample rate */
+ DWORD nAvgBytesPerSec; /* for buffer estimation */
+ WORD nBlockAlign; /* block size of data */
+ WORD wBitsPerSample; /* number of bits per sample of mono data */
+ WORD cbSize; /* the count in bytes of the size of */
+ /* extra information (after cbSize) */
+} WAVEFORMATEX, *PWAVEFORMATEX, NEAR *NPWAVEFORMATEX, FAR *LPWAVEFORMATEX ;
+
+typedef const WAVEFORMATEX FAR *LPCWAVEFORMATEX ;
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef _MSC_VER
+ #pragma pack(pop) /* revert to previous packing */
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#endif
+
+#endif
diff --git a/Src/replicant/nsmp3dec/sequencedetector.h b/Src/replicant/nsmp3dec/sequencedetector.h
new file mode 100644
index 000000000..b59b27f4e
--- /dev/null
+++ b/Src/replicant/nsmp3dec/sequencedetector.h
@@ -0,0 +1,53 @@
+/***************************************************************************\
+ *
+* MPEG Layer3-Audio Decoder
+* © 1997-2006 by Fraunhofer IIS
+ * All Rights Reserved
+ *
+ * filename: sequencedetector.h
+ * project : ---
+ * author : Martin Sieler
+ * date : 1998-02-14
+ * contents/description: HEADER - sequence detector
+ *
+ *
+\***************************************************************************/
+
+/*
+ * $Date: 2010/11/17 20:46:05 $
+ * $Id: sequencedetector.h,v 1.1 2010/11/17 20:46:05 audiodsp Exp $
+ */
+
+#ifndef __SEQUENCEDETECTOR_H__
+#define __SEQUENCEDETECTOR_H__
+
+/* ------------------------ includes --------------------------------------*/
+
+/*-------------------------- defines --------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+class CSequenceDetector
+{
+public:
+ CSequenceDetector(int nLimit);
+ ~CSequenceDetector();
+
+ void Reset();
+ CSequenceDetector& operator+= (int nValue);
+
+ int GetLength() const;
+ int GetValue(int nIndex) const;
+ int GetSum() const;
+
+protected:
+
+private:
+ int m_Limit;
+ int m_Count;
+ bool *m_pDisabled;
+ int *m_pArray;
+};
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/Src/replicant/nswasabi/APEv2Metadata.cpp b/Src/replicant/nswasabi/APEv2Metadata.cpp
new file mode 100644
index 000000000..979a987aa
--- /dev/null
+++ b/Src/replicant/nswasabi/APEv2Metadata.cpp
@@ -0,0 +1,306 @@
+#include "APEv2Metadata.h"
+#include "metadata/MetadataKeys.h"
+#include "nu/ByteReader.h"
+#include "nswasabi/ReferenceCounted.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+static inline bool TestFlag(int flags, int flag_to_check)
+{
+ if (flags & flag_to_check)
+ return true;
+ return false;
+}
+
+api_metadata *APEv2Metadata::metadata_api=0;
+
+APEv2Metadata::APEv2Metadata()
+{
+ apev2_tag=0;
+}
+
+APEv2Metadata::~APEv2Metadata()
+{
+}
+
+int APEv2Metadata::Initialize(api_metadata *metadata_api)
+{
+ APEv2Metadata::metadata_api = metadata_api;
+ return NErr_Success;
+}
+
+int APEv2Metadata::Initialize(nsapev2_tag_t tag)
+{
+ apev2_tag = tag;
+ return NErr_Success;
+}
+
+
+/* ifc_metadata implementation */
+int APEv2Metadata::Metadata_GetField(int field, unsigned int index, nx_string_t *value)
+{
+ if (!apev2_tag)
+ return NErr_Unknown;
+
+ switch (field)
+ {
+ case MetadataKeys::TRACK_GAIN:
+ return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_GAIN", index, value);
+ case MetadataKeys::TRACK_PEAK:
+ return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_PEAK", index, value);
+ case MetadataKeys::ALBUM_GAIN:
+ return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_GAIN", index, value);
+ case MetadataKeys::ALBUM_PEAK:
+ return NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_PEAK", index, value);
+ }
+
+ return NErr_Unknown;
+}
+
+int APEv2Metadata::Metadata_GetInteger(int field, unsigned int index, int64_t *value)
+{
+ if (!apev2_tag)
+ return NErr_Unknown;
+
+ return NErr_Unknown;
+}
+
+int APEv2Metadata::Metadata_GetReal(int field, unsigned int index, double *value)
+{
+ if (!apev2_tag)
+ return NErr_Unknown;
+
+ int ret;
+ nx_string_t str;
+ switch (field)
+ {
+ case MetadataKeys::TRACK_GAIN:
+ ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_GAIN", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ case MetadataKeys::TRACK_PEAK:
+ ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_TRACK_PEAK", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ case MetadataKeys::ALBUM_GAIN:
+ ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_GAIN", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ case MetadataKeys::ALBUM_PEAK:
+ ret = NSAPEv2_Tag_GetString(apev2_tag, "REPLAYGAIN_ALBUM_PEAK", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ }
+ return NErr_Unknown;
+}
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+
+static const char *APEv2_GetMIME(const char *extension)
+{
+ if (!extension)
+ return 0;
+
+ if (strcasecmp(extension, "JPG") == 0 || strcasecmp(extension, "JPEG") == 0)
+ return "image/jpeg";
+ if (strcasecmp(extension, "PNG") == 0)
+ return "image/png";
+ if (strcasecmp(extension, "GIF") == 0)
+ return "image/gif";
+ if (strcasecmp(extension, "BMP") == 0)
+ return "image/bmp";
+
+ return 0;
+}
+
+static int APEv2_ParseArt(const void *bytes, size_t length, artwork_t *out_data, data_flags_t flags)
+{
+ if (out_data)
+ {
+ nx_data_t data=0;
+ if (flags != DATA_FLAG_NONE)
+ {
+ bytereader_s byte_reader;
+ bytereader_init(&byte_reader, bytes, length);
+ if (bytereader_size(&byte_reader) == 0)
+ return NErr_Insufficient;
+
+ const char *description_start = (const char *)bytereader_pointer(&byte_reader);
+ const char *extension_start=0;
+ uint8_t byte;
+ do
+ {
+ if (bytereader_size(&byte_reader) == 0)
+ return NErr_Insufficient;
+ byte = bytereader_read_u8(&byte_reader);
+ if (byte == '.') // found extension
+ {
+ extension_start = (const char *)bytereader_pointer(&byte_reader);
+ }
+ } while (byte && bytereader_size(&byte_reader));
+
+ size_t length = bytereader_size(&byte_reader);
+
+ if (length == 0)
+ return NErr_Empty;
+
+ if (TestFlag(flags, DATA_FLAG_DATA))
+ {
+ int ret = NXDataCreate(&data, bytereader_pointer(&byte_reader), length);
+ if (ret != NErr_Success)
+ return ret;
+ }
+ else
+ {
+ int ret = NXDataCreateEmpty(&data);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ if (TestFlag(flags, DATA_FLAG_DESCRIPTION))
+ {
+ ReferenceCountedNXString description;
+ size_t length;
+ if (extension_start)
+ length = (size_t)extension_start - (size_t)description_start - 1;
+ else
+ length = (size_t)bytereader_pointer(&byte_reader) - (size_t)description_start - 1;
+
+ if (length)
+ {
+ int ret = NXStringCreateWithBytes(&description, description_start, length, nx_charset_utf8);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ NXDataSetDescription(data, description);
+ }
+ }
+
+ if (TestFlag(flags, DATA_FLAG_MIME))
+ {
+ ReferenceCountedNXString mime_type;
+ const char *mime_string = APEv2_GetMIME(extension_start);
+ if (mime_string)
+ {
+ int ret = NXStringCreateWithUTF8(&mime_type, mime_string);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+ }
+ }
+ out_data->data = data;
+ /* we don't know these */
+ out_data->height=0;
+ out_data->width=0;
+ }
+ return NErr_Success;
+}
+
+int APEv2Metadata::Metadata_GetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags)
+{
+ if (!apev2_tag)
+ return NErr_Unknown;
+
+ int ret;
+ const void *bytes;
+ size_t length;
+ switch(field)
+ {
+ case MetadataKeys::ALBUM:
+ ret = NSAPEv2_Tag_GetBinary(apev2_tag, "Cover Art (front)", index, &bytes, &length);
+ if (ret == NErr_Success)
+ return APEv2_ParseArt(bytes, length, data, flags);
+ return ret;
+ }
+ return NErr_Unknown;
+}
+
+int APEv2Metadata::MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)
+{
+ switch (field)
+ {
+ case MetadataKeys::TRACK_GAIN:
+ return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_TRACK_GAIN", index, value);
+ case MetadataKeys::TRACK_PEAK:
+ return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_TRACK_PEAK", index, value);
+ case MetadataKeys::ALBUM_GAIN:
+ return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_ALBUM_GAIN", index, value);
+ case MetadataKeys::ALBUM_PEAK:
+ return NSAPEv2_Tag_SetString(apev2_tag, "REPLAYGAIN_ALBUM_PEAK", index, value);
+ }
+ return NErr_Unknown;
+}
+
+int APEv2Metadata::MetadataEditor_SetInteger(int field, unsigned int index, int64_t value)
+{
+ return NErr_Unknown;
+}
+
+int APEv2Metadata::MetadataEditor_SetReal(int field, unsigned int index, double value)
+{
+ // TODO: but we need NXStringCreateFromDouble which I don't feel like writing right now
+ return NErr_Unknown;
+}
+
+static void APEv2_GetFilenameForMIME(char *filename, const char *type, nx_string_t mime_type)
+{
+ if (mime_type)
+ {
+ if (NXStringKeywordCompareWithCString(mime_type, "image/jpeg") == NErr_True || NXStringKeywordCompareWithCString(mime_type, "image/jpg") == NErr_True)
+ sprintf(filename, "%s.jpeg", type);
+ else if (NXStringKeywordCompareWithCString(mime_type, "image/png") == NErr_True)
+ sprintf(filename, "%s.png", type);
+ if (NXStringKeywordCompareWithCString(mime_type, "image/gif") == NErr_True)
+ sprintf(filename, "%s.gif", type);
+ if (NXStringKeywordCompareWithCString(mime_type, "image/bmp") == NErr_True)
+ sprintf(filename, "%s.bmp", type);
+ else
+ sprintf(filename, "%s.jpg", type); // TODO: perhaps we could use whatever is after image/
+ }
+ else
+ sprintf(filename, "%s.jpg", type); // ehh, just guess
+}
+
+int APEv2Metadata::MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags)
+{
+ ReferenceCountedNXString mime_type;
+ const void *bytes = 0;
+ size_t length = 0;
+ switch(field)
+ {
+ case MetadataKeys::ALBUM:
+ if (data && NXDataGet(data->data, &bytes, &length) == NErr_Success)
+ {
+ char filename[256] = {0};
+ NXDataGetMIME(data->data, &mime_type);
+ APEv2_GetFilenameForMIME(filename, "cover", mime_type); /* TODO: perhaps use description, instead? */
+ return NSAPEv2_Tag_SetArtwork(apev2_tag, "Cover Art (front)", index, filename, bytes, length);
+ }
+ else
+ return NSAPEv2_Tag_SetArtwork(apev2_tag, "Cover Art (front)", index, 0, 0, 0);
+ }
+ return NErr_Unknown;
+}
diff --git a/Src/replicant/nswasabi/APEv2Metadata.h b/Src/replicant/nswasabi/APEv2Metadata.h
new file mode 100644
index 000000000..ba4e15ac8
--- /dev/null
+++ b/Src/replicant/nswasabi/APEv2Metadata.h
@@ -0,0 +1,29 @@
+#pragma once
+#include "metadata/metadata.h"
+#include "nsapev2/nsapev2.h"
+
+/* this class mimics ifc_metadata and ifc_metadata_editor, but doesn't inherit (because it's not given out directly) */
+class APEv2Metadata
+{
+public:
+ APEv2Metadata();
+ ~APEv2Metadata();
+
+ static int Initialize(api_metadata *metadata_api);
+ int Initialize(nsapev2_tag_t tag);
+
+ /* ifc_metadata implementation */
+ 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);
+
+ /* ifc_metadata_editor implementation */
+ 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 *data, data_flags_t flags);
+private:
+ nsapev2_tag_t apev2_tag;
+ static api_metadata *metadata_api;
+};
diff --git a/Src/replicant/nswasabi/Android.mk b/Src/replicant/nswasabi/Android.mk
new file mode 100644
index 000000000..396822213
--- /dev/null
+++ b/Src/replicant/nswasabi/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := nswasabi
+LOCAL_ARM_MODE := arm
+LOCAL_C_INCLUDES := $(ROOT_REPLICANT)
+LOCAL_CFLAGS := -fvisibility=hidden
+LOCAL_SRC_FILES := PlaybackBase.cpp XMLString.cpp ID3v2Metadata.cpp ID3v1Metadata.cpp APEv2Metadata.cpp ApplicationBase.cpp
+LOCAL_STATIC_LIBRARIES := nu
+LOCAL_EXPORT_LDLIBS := -llog
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/Src/replicant/nswasabi/ApplicationBase.cpp b/Src/replicant/nswasabi/ApplicationBase.cpp
new file mode 100644
index 000000000..334c3e240
--- /dev/null
+++ b/Src/replicant/nswasabi/ApplicationBase.cpp
@@ -0,0 +1,168 @@
+#include "ApplicationBase.h"
+#include "foundation/error.h"
+#include "application/features.h"
+#include <stdio.h> // for sprintf
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
+ApplicationBase::ApplicationBase()
+{
+ data_path = 0;
+ all_permissions_enabled = false;
+ device_id = 0;
+}
+
+ApplicationBase::~ApplicationBase()
+{
+ NXURIRelease(data_path);
+ data_path = 0;
+ NXStringRelease(device_id);
+ device_id = 0;
+}
+
+int ApplicationBase::Initialize()
+{
+ return NErr_Success;
+}
+
+/* and call this after doing your own shutdown */
+void ApplicationBase::Shutdown()
+{
+ NXURIRelease(data_path);
+ data_path = 0;
+}
+
+void ApplicationBase::SetDataPath(nx_uri_t new_data_path)
+{
+ nx_uri_t old_path = data_path;
+ data_path = NXURIRetain(new_data_path);
+ NXURIRelease(old_path);
+}
+
+void ApplicationBase::SetPermission(GUID feature)
+{
+ permissions.insert(feature);
+}
+
+void ApplicationBase::RemovePermission(GUID permission)
+{
+ permissions.erase(permission);
+}
+
+void ApplicationBase::EnableAllPermissions()
+{
+ all_permissions_enabled=true;
+}
+
+void ApplicationBase::ClearPermissions()
+{
+ permissions.clear();
+}
+
+void ApplicationBase::NotifyPermissions(api_syscb *system_callbacks)
+{
+ if (system_callbacks)
+ system_callbacks->IssueCallback(Features::event_type, Features::permissions_changed);
+}
+
+int ApplicationBase::Application_GetDataPath(nx_uri_t *path)
+{
+ *path=NXURIRetain(data_path);
+ if (data_path)
+ return NErr_Success;
+ else
+ return NErr_Empty;
+}
+
+int ApplicationBase::Application_GetPermission(GUID feature)
+{
+ if (all_permissions_enabled)
+ return NErr_True;
+ else if (permissions.find(feature) == permissions.end())
+ return NErr_False;
+ else
+ return NErr_True;
+}
+
+int ApplicationBase::Application_GetFeature(GUID feature)
+{
+ if (features.find(feature) == features.end())
+ return NErr_False;
+ else
+ return NErr_True;
+}
+
+void ApplicationBase::Application_SetFeature(GUID feature)
+{
+ features.insert(feature);
+}
+
+void ApplicationBase::SetDeviceID(nx_string_t device_id)
+{
+ nx_string_t old = this->device_id;
+ this->device_id = NXStringRetain(device_id);
+ NXStringRelease(old);
+}
+
+static void GUIDtoCString(const GUID &guid, char *target)
+{
+ //{2E9CE2F8-E26D-4629-A3FF-5DF619136B2C}
+ sprintf(target, "{%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x}",
+ (int)guid.Data1, (int)guid.Data2, (int)guid.Data3,
+ (int)guid.Data4[0], (int)guid.Data4[1],
+ (int)guid.Data4[2], (int)guid.Data4[3],
+ (int)guid.Data4[4], (int)guid.Data4[5],
+ (int)guid.Data4[6], (int)guid.Data4[7] );
+
+}
+
+static const char *GetFeatureName(const GUID &guid)
+{
+ if (guid == Features::aac_playback)
+ return "AAC Playback";
+ else if (guid == Features::gapless)
+ return "Gapless Playback";
+ else if (guid == Features::flac_playback)
+ return "FLAC Playback";
+ else if (guid == Features::gracenote_autotag)
+ return "Gracenote Autotagger";
+ else
+ return 0; /* the lack of of return 0 by default here was why it was crashing */
+
+}
+
+void ApplicationBase::DumpPermissions()
+{
+#ifdef __ANDROID__
+ char guid_string[64];
+ FeatureList::iterator itr;
+ for (itr=features.begin();itr!=features.end();itr++)
+ {
+ GUIDtoCString(*itr, guid_string);
+ const char *feature_name = GetFeatureName(*itr);
+ if (feature_name)
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[Feature] %s (%s)", guid_string, feature_name);
+ else
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[Feature] %s", guid_string);
+ }
+
+ for (itr=permissions.begin();itr!=permissions.end();itr++)
+ {
+ GUIDtoCString(*itr, guid_string);
+ const char *feature_name = GetFeatureName(*itr);
+ if (feature_name)
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[Permission] %s (%s)", guid_string, feature_name);
+ else
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[Permission] %s", guid_string);
+ }
+#endif
+}
+
+int ApplicationBase::Application_GetDeviceID(nx_string_t *value)
+{
+ if (!device_id)
+ return NErr_Empty;
+ *value = NXStringRetain(device_id);
+ return NErr_Success;
+}
diff --git a/Src/replicant/nswasabi/ApplicationBase.h b/Src/replicant/nswasabi/ApplicationBase.h
new file mode 100644
index 000000000..f82f0b3af
--- /dev/null
+++ b/Src/replicant/nswasabi/ApplicationBase.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "application/api_application.h"
+#include <set>
+#include "syscb/api_syscb.h"
+
+/* implements non-platform-specific methods of api_application.
+You can derive your Application implementation from this to ease your life */
+class ApplicationBase : public api_application
+{
+public:
+ ApplicationBase();
+ ~ApplicationBase();
+
+ /* call this (and check the return value) before doing your own initialization */
+ int Initialize();
+
+ /* and call this after doing your own shutdown */
+ void Shutdown();
+
+ void SetDataPath(nx_uri_t data_path);
+ void SetPermission(GUID feature);
+ void RemovePermission(GUID permission);
+ void SetDeviceID(nx_string_t device_id);
+ void EnableAllPermissions();
+ void ClearPermissions();
+ void NotifyPermissions(api_syscb *system_callbacks); /* pass in the syscb API to avoid a dependency */
+ void DumpPermissions(); /* dumps permissions list to the log file */
+protected:
+ /* api_application implementation */
+ int Application_GetDataPath(nx_uri_t *path);
+ int Application_GetPermission(GUID feature);
+ int Application_GetFeature(GUID feature);
+ void Application_SetFeature(GUID feature);
+ int Application_GetDeviceID(nx_string_t *value);
+
+private:
+ typedef std::set<GUID> FeatureList;
+ FeatureList features;
+ FeatureList permissions;
+ bool all_permissions_enabled; /* bypass for developer/QA testing */
+ nx_uri_t data_path;
+ nx_string_t device_id;
+
+};
diff --git a/Src/replicant/nswasabi/AutoCharNX.h b/Src/replicant/nswasabi/AutoCharNX.h
new file mode 100644
index 000000000..86e83b844
--- /dev/null
+++ b/Src/replicant/nswasabi/AutoCharNX.h
@@ -0,0 +1,184 @@
+#pragma once
+#include "../nx/nxstring.h"
+#include "../nx/nxuri.h"
+#include "../foundation/error.h"
+#include <stdlib.h>
+
+template <nx_charset_t charset>
+class AutoCharNX
+{
+public:
+ AutoCharNX()
+ {
+ Init();
+ }
+
+ AutoCharNX(size_t bytes)
+ {
+ Init();
+ ptr = (char *)malloc(bytes);
+ malloc_size = bytes;
+ }
+
+ AutoCharNX(nx_string_t string)
+ {
+ Init();
+
+ Set(string);
+ }
+
+ AutoCharNX(nx_uri_t filename)
+ {
+ Init();
+
+ Set(filename);
+ }
+
+ ~AutoCharNX()
+ {
+ if (owned)
+ free(ptr);
+ if (reference_string)
+ NXStringRelease(reference_string);
+ }
+
+ int Set(nx_string_t string)
+ {
+ if (reference_string == string)
+ return NErr_Success;
+
+ if (reference_string)
+ NXStringRelease(reference_string);
+ reference_string=0;
+
+ size_t byte_count=0;
+ int ret = NXStringGetBytesSize(&byte_count, string, charset, nx_string_get_bytes_size_null_terminate);
+ if(ret == NErr_DirectPointer)
+ {
+ if (owned)
+ {
+ free(ptr);
+ ptr=0;
+ length=0;
+ malloc_size=0;
+ }
+ ret = NXStringGetBytesDirect((const void **)&ptr, &length, string, charset, nx_string_get_bytes_size_null_terminate);
+ reference_string = NXStringRetain(string);
+ owned=false;
+ }
+ else if (ret == NErr_Success)
+ {
+ if (owned)
+ {
+ if (byte_count > malloc_size)
+ {
+ ptr = (char *)realloc(ptr, byte_count);
+ malloc_size = byte_count;
+ }
+ }
+ else
+ {
+ /* not owned. need to allocate */
+ ptr = (char *)malloc(byte_count);
+ malloc_size = byte_count;
+ owned=true;
+ }
+
+ if (ptr)
+ {
+ ret = NXStringGetBytes(&length, string, ptr, byte_count, charset, nx_string_get_bytes_size_null_terminate);
+ }
+ else
+ {
+ return NErr_OutOfMemory;
+ }
+ }
+ else
+ {
+ Clear();
+ }
+ return ret;
+ }
+
+ int Set(nx_uri_t filename)
+ {
+ int ret;
+ nx_string_t string;
+ ret = NXURIGetNXString(&string, filename);
+ if (ret == NErr_Success)
+ {
+ ret = Set(string);
+ NXStringRelease(string);
+ }
+ else
+ {
+ Clear();
+ // failed! we need to clean up
+ }
+ return ret;
+ }
+
+ operator const char *() const
+ {
+ if (length)
+ return ptr;
+ else
+ return 0;
+ }
+
+ /* this one will never return a NULL, always a valid string */
+ const char *GetValidString() const
+ {
+ if (length)
+ return ptr;
+ else
+ return "";
+ }
+
+ /* the Clear function clears the string but doesn't deallocate memory */
+ void Clear()
+ {
+ if (!owned)
+ ptr=0;
+ length=0;
+
+ if (reference_string)
+ NXStringRelease(reference_string);
+ reference_string=0;
+ }
+
+ size_t size()
+ {
+ if (length)
+ return length-1;
+ else
+ return 0;
+ }
+private:
+ void Init()
+ {
+ ptr=0;
+ length=0;
+ owned=false;
+ reference_string=0;
+ malloc_size=0;
+ }
+ char *ptr;
+ size_t length;
+ size_t malloc_size;
+ bool owned;
+ nx_string_t reference_string;
+};
+
+typedef AutoCharNX<nx_charset_utf8> AutoCharUTF8;
+#define AutoCharPrintfUTF8(x) (AutoCharUTF8(x).GetValidString())
+class AutoCharNative
+{
+public:
+};
+
+class AutoFilename
+{
+public:
+};
+
diff --git a/Src/replicant/nswasabi/ComponentManagerBase.cpp b/Src/replicant/nswasabi/ComponentManagerBase.cpp
new file mode 100644
index 000000000..aa8d3f43d
--- /dev/null
+++ b/Src/replicant/nswasabi/ComponentManagerBase.cpp
@@ -0,0 +1,145 @@
+#include "ComponentManagerBase.h"
+#include "foundation/error.h"
+#include "nx/nxuri.h"
+
+ComponentManagerBase::ComponentManagerBase()
+{
+ phase=PHASE_INITIALIZE;
+ service_api=0;
+ component_sync=0;
+}
+
+int ComponentManagerBase::LateLoad(ifc_component *component)
+{
+ int ret;
+
+ if (phase >= PHASE_REGISTERED)
+ {
+ ret = component->RegisterServices(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ return ret;
+ }
+ }
+
+ if (phase >= PHASE_LOADING)
+ {
+ ret = component->OnLoading(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ return ret;
+ }
+ }
+
+ if (phase >= PHASE_LOADED)
+ {
+ ret = component->OnLoaded(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ return ret;
+ }
+ }
+ return NErr_Success;
+}
+
+void ComponentManagerBase::SetServiceAPI(api_service *service_api)
+{
+ this->service_api = service_api;
+ service_api->QueryInterface(&component_sync);
+}
+
+int ComponentManagerBase::Load()
+{
+ if (phase != PHASE_INITIALIZE)
+ return NErr_Error;
+
+ int ret;
+
+ /* RegisterServices phase */
+ for (ComponentList::iterator itr=components.begin();itr!=components.end();)
+ {
+ ifc_component *component = *itr;
+ ComponentList::iterator next=itr;
+ next++;
+ ret = component->RegisterServices(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ NXURIRelease(component->component_info.filename);
+ CloseComponent(component);
+ components.erase(component);
+
+ }
+ itr=next;
+ }
+
+ phase = PHASE_REGISTERED;
+
+ /* OnLoading phase */
+ for (ComponentList::iterator itr=components.begin();itr!=components.end();)
+ {
+ ifc_component *component = *itr;
+ ComponentList::iterator next=itr;
+ next++;
+ ret = component->OnLoading(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ NXURIRelease(component->component_info.filename);
+ CloseComponent(component);
+ components.erase(component);
+
+ }
+ itr=next;
+ }
+
+ phase = PHASE_LOADING;
+
+ /* OnLoaded phase */
+ for (ComponentList::iterator itr=components.begin();itr!=components.end();)
+ {
+ ifc_component *component = *itr;
+ ComponentList::iterator next=itr;
+ next++;
+ ret = component->OnLoading(service_api);
+ if (ret != NErr_Success)
+ {
+ int ret2 = component->Quit(service_api);
+ if (ret2 == NErr_TryAgain)
+ {
+ component_sync->Wait(1);
+ }
+ NXURIRelease(component->component_info.filename);
+ CloseComponent(component);
+ components.erase(component);
+ }
+ itr=next;
+ }
+
+ phase = PHASE_LOADED;
+
+ return NErr_Success;
+}
diff --git a/Src/replicant/nswasabi/ComponentManagerBase.h b/Src/replicant/nswasabi/ComponentManagerBase.h
new file mode 100644
index 000000000..00252b36c
--- /dev/null
+++ b/Src/replicant/nswasabi/ComponentManagerBase.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "nx/nxuri.h"
+#include "service/api_service.h"
+#include "component/ifc_component.h"
+#include "nu/PtrDeque.h"
+#include "component/ifc_component_sync.h"
+
+class ComponentManagerBase
+{
+public:
+ void SetServiceAPI(api_service *service_api);
+ int Load();
+protected:
+ ComponentManagerBase();
+ int LateLoad(ifc_component *mod);
+ enum Phase
+ {
+ PHASE_INITIALIZE=0, /* components are still being added */
+ PHASE_REGISTERED=1, /* RegisterServices() has been called on all components */
+ PHASE_LOADING=2, /* OnLoading() has been called on all components */
+ PHASE_LOADED=3, /* OnLoaded() has been called on all components */
+ };
+ Phase phase;
+ typedef nu::PtrDeque<ifc_component> ComponentList;
+ ComponentList components;
+ api_service *service_api;
+ ifc_component_sync *component_sync;
+private:
+ /* your implementation needs to override this. You should call FreeLibrary(component->component_info.hModule); or dlclose(component->component_info.dl_handle); or similar */
+ virtual void CloseComponent(ifc_component *component)=0;
+};
diff --git a/Src/replicant/nswasabi/ID3v1Metadata.cpp b/Src/replicant/nswasabi/ID3v1Metadata.cpp
new file mode 100644
index 000000000..2883401ec
--- /dev/null
+++ b/Src/replicant/nswasabi/ID3v1Metadata.cpp
@@ -0,0 +1,187 @@
+#include "ID3v1Metadata.h"
+#include "metadata/MetadataKeys.h"
+#include <stdlib.h>
+
+api_metadata *ID3v1Metadata::metadata_api=0;
+
+ID3v1Metadata::ID3v1Metadata()
+{
+ id3v1_tag=0;
+}
+
+ID3v1Metadata::~ID3v1Metadata()
+{
+}
+
+int ID3v1Metadata::Initialize(api_metadata *metadata_api)
+{
+ ID3v1Metadata::metadata_api = metadata_api;
+ return NErr_Success;
+}
+
+int ID3v1Metadata::Initialize(nsid3v1_tag_t tag)
+{
+ id3v1_tag = tag;
+ this->metadata_api = metadata_api;
+ return NErr_Success;
+}
+
+/* ifc_metadata implementation */
+int ID3v1Metadata::Metadata_GetField(int field, unsigned int index, nx_string_t *value)
+{
+ if (!id3v1_tag)
+ return NErr_Unknown;
+
+ switch (field)
+ {
+ case MetadataKeys::TITLE:
+ return index?NErr_EndOfEnumeration:NSID3v1_Get_Title(id3v1_tag, value);
+ case MetadataKeys::ARTIST:
+ return index?NErr_EndOfEnumeration:NSID3v1_Get_Artist(id3v1_tag, value);
+ case MetadataKeys::ALBUM:
+ return index?NErr_EndOfEnumeration:NSID3v1_Get_Album(id3v1_tag, value);
+ case MetadataKeys::YEAR:
+ return index?NErr_EndOfEnumeration:NSID3v1_Get_Year(id3v1_tag, value);
+ case MetadataKeys::COMMENT:
+ return index?NErr_EndOfEnumeration:NSID3v1_Get_Comment(id3v1_tag, value);
+ case MetadataKeys::TRACK:
+ return index?NErr_EndOfEnumeration:NSID3v1_Get_Track(id3v1_tag, value);
+ case MetadataKeys::GENRE:
+ {
+ if (!metadata_api)
+ return NErr_Unknown;
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ uint8_t genre_id;
+ int ret = NSID3v1_Int_Get_Genre(id3v1_tag, &genre_id);
+ if (ret != NErr_Success)
+ return ret;
+
+ nx_string_t genre;
+ ret = metadata_api->GetGenre(genre_id, &genre);
+ if (ret == NErr_Success)
+ {
+ *value = NXStringRetain(genre);
+ return NErr_Success;
+ }
+ else if (ret == NErr_Unknown)
+ {
+ return NErr_Empty;
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ }
+
+ return NErr_Unknown;
+}
+
+int ID3v1Metadata::Metadata_GetInteger(int field, unsigned int index, int64_t *value)
+{
+ if (!id3v1_tag)
+ return NErr_Unknown;
+
+ switch (field)
+ {
+ case MetadataKeys::YEAR:
+ {
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+ unsigned int year;
+ int ret = NSID3v1_Int_Get_Year(id3v1_tag, &year);
+ if (ret == NErr_Success)
+ *value = (int64_t)year;
+ return ret;
+ }
+ case MetadataKeys::TRACK:
+ {
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+ uint8_t track;
+ int ret = NSID3v1_Int_Get_Track(id3v1_tag, &track);
+ if (ret == NErr_Success)
+ *value = (int64_t)track;
+ return ret;
+ }
+ }
+ return NErr_Unknown;
+}
+
+int ID3v1Metadata::Metadata_GetReal(int field, unsigned int index, double *value)
+{
+ if (!id3v1_tag)
+ return NErr_Unknown;
+
+ return NErr_Unknown;
+}
+
+int ID3v1Metadata::MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)
+{
+ if (!id3v1_tag)
+ return NErr_NullPointer;
+
+ switch (field)
+ {
+ case MetadataKeys::TITLE:
+ return index?NErr_EndOfEnumeration:NSID3v1_Set_Title(id3v1_tag, value);
+ case MetadataKeys::ARTIST:
+ return index?NErr_EndOfEnumeration:NSID3v1_Set_Artist(id3v1_tag, value);
+ case MetadataKeys::ALBUM:
+ return index?NErr_EndOfEnumeration:NSID3v1_Set_Album(id3v1_tag, value);
+ case MetadataKeys::YEAR:
+ return index?NErr_EndOfEnumeration:NSID3v1_Set_Year(id3v1_tag, value);
+ case MetadataKeys::COMMENT:
+ return index?NErr_EndOfEnumeration:NSID3v1_Set_Comment(id3v1_tag, value);
+ case MetadataKeys::TRACK:
+ return index?NErr_EndOfEnumeration:NSID3v1_Set_Track(id3v1_tag, value);
+
+ case MetadataKeys::GENRE:
+ {
+ if (!metadata_api)
+ return NErr_Unknown;
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ uint8_t genre_id;
+ int ret = metadata_api->GetGenreID(value, &genre_id);
+ if (ret == NErr_Success)
+ return NSID3v1_Int_Set_Genre(id3v1_tag, genre_id);
+ else
+ return NSID3v1_Int_Set_Genre(id3v1_tag, 0xFF);
+ }
+
+
+ }
+
+ return NErr_Unknown;
+}
+
+int ID3v1Metadata::MetadataEditor_SetInteger(int field, unsigned int index, int64_t value)
+{
+ if (!id3v1_tag)
+ return NErr_NullPointer;
+
+ if (index != 0)
+ return NErr_EndOfEnumeration;
+
+ switch (field)
+ {
+ case MetadataKeys::YEAR:
+ return NSID3v1_Int_Set_Year(id3v1_tag, (unsigned int)value);
+ case MetadataKeys::TRACK:
+ if (value < 0 || value > 255)
+ return NErr_ParameterOutOfRange;
+ return NSID3v1_Int_Set_Track(id3v1_tag, (uint8_t)value);
+ case MetadataKeys::GENRE:
+ if (value < 0 || value > 255)
+ return NErr_ParameterOutOfRange;
+ return NSID3v1_Int_Set_Genre(id3v1_tag, (uint8_t)value);
+ }
+
+ return NErr_Unknown;
+}
+
+#undef DESCRIPTION
diff --git a/Src/replicant/nswasabi/ID3v1Metadata.h b/Src/replicant/nswasabi/ID3v1Metadata.h
new file mode 100644
index 000000000..c6da027cf
--- /dev/null
+++ b/Src/replicant/nswasabi/ID3v1Metadata.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "metadata/metadata.h"
+#include "nsid3v1/nsid3v1.h"
+
+/* this class mimics ifc_metadata and ifc_metadata_editor, but doesn't inherit (because it's not given out directly) */
+class ID3v1Metadata
+{
+public:
+ ID3v1Metadata();
+ ~ID3v1Metadata();
+
+ static int Initialize(api_metadata *metadata_api);
+ int Initialize(nsid3v1_tag_t tag);
+
+ /* ifc_metadata implementation */
+ 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);
+
+ /* ifc_metadata_editor implementation */
+ 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);
+private:
+ nsid3v1_tag_t id3v1_tag;
+ static api_metadata *metadata_api;
+};
diff --git a/Src/replicant/nswasabi/ID3v2Metadata.cpp b/Src/replicant/nswasabi/ID3v2Metadata.cpp
new file mode 100644
index 000000000..a1b49212e
--- /dev/null
+++ b/Src/replicant/nswasabi/ID3v2Metadata.cpp
@@ -0,0 +1,1087 @@
+#include "ID3v2Metadata.h"
+#include "metadata/MetadataKeys.h"
+#include "nswasabi/ReferenceCounted.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+api_metadata *ID3v2Metadata::metadata_api=0;
+
+static inline bool TestFlag(int flags, int flag_to_check)
+{
+ if (flags & flag_to_check)
+ return true;
+ return false;
+}
+
+ID3v2Metadata::ID3v2Metadata()
+{
+ id3v2_tag=0;
+
+#ifdef __APPLE__
+ number_formatter = NULL;
+#endif
+}
+
+ID3v2Metadata::~ID3v2Metadata()
+{
+#ifdef __APPLE__
+ if (NULL != number_formatter)
+ CFRelease(number_formatter);
+#endif
+}
+
+int ID3v2Metadata::Initialize(api_metadata *metadata_api)
+{
+ ID3v2Metadata::metadata_api = metadata_api;
+ return NErr_Success;
+}
+
+int ID3v2Metadata::Initialize(nsid3v2_tag_t tag)
+{
+ id3v2_tag = tag;
+
+ return NErr_Success;
+}
+
+int ID3v2Metadata::GetGenre(int index, nx_string_t *value)
+{
+ nx_string_t genre=0;
+ int ret = NSID3v2_Tag_Text_Get(id3v2_tag, NSID3V2_FRAME_CONTENTTYPE, &genre, 0);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ if (genre)
+ {
+ *value = genre;
+#ifdef _WIN32
+ // parse the (##) out of it
+ wchar_t *tmp = genre->string;
+ while (*tmp == ' ') tmp++;
+ if (!wcsncmp(tmp, L"(RX)", 4))
+ {
+ *value = NXStringCreateFromUTF8("Remix");
+ NXStringRelease(genre);
+ if (*value)
+ return NErr_Success;
+ else
+ return NErr_OutOfMemory;
+ }
+ else if (!wcsncmp(tmp, L"(CR)", 4))
+ {
+ *value = NXStringCreateFromUTF8("Cover");
+ NXStringRelease(genre);
+ if (*value)
+ return NErr_Success;
+ else
+ return NErr_OutOfMemory;
+ }
+
+ if (*tmp == '(' || (*tmp >= '0' && *tmp <= '9')) // both (%d) and %d forms
+ {
+ int noparam = 0;
+
+ if (*tmp == '(') tmp++;
+ else noparam = 1;
+ size_t genre_index = _wtoi(tmp);
+ int cnt = 0;
+ while (*tmp >= '0' && *tmp <= '9') cnt++, tmp++;
+ while (*tmp == ' ') tmp++;
+
+ if (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0)
+ {
+ if (genre_index < 256 && metadata_api)
+ {
+ int ret = metadata_api->GetGenre(genre_index, value);
+ if (ret == NErr_Success)
+ {
+ NXStringRetain(*value);
+ NXStringRelease(genre);
+ return ret;
+ }
+ }
+ }
+ }
+#elif defined(__APPLE__)
+ int ret = NErr_Success;
+
+ CFMutableStringRef mutable_genre = CFStringCreateMutableCopy(NULL, 0, genre);
+ CFStringTrimWhitespace(mutable_genre);
+
+ CFIndex mutable_genre_length = CFStringGetLength(mutable_genre);
+
+ if (kCFCompareEqualTo == CFStringCompareWithOptionsAndLocale(mutable_genre,
+ CFSTR("(RX)"),
+ CFRangeMake(0, mutable_genre_length),
+ 0,
+ NULL))
+ {
+ NXStringRelease(genre);
+ *value = CFSTR("Remix");
+ ret = NErr_Success;
+ }
+ else if (kCFCompareEqualTo == CFStringCompareWithOptionsAndLocale(mutable_genre,
+ CFSTR("(CR)"),
+ CFRangeMake(0, mutable_genre_length),
+ 0,
+ NULL))
+ {
+ NXStringRelease(genre);
+ *value = CFSTR("Cover");
+ ret = NErr_Success;
+ }
+ else
+ {
+ CFStringTrim(mutable_genre, CFSTR("("));
+ CFStringTrim(mutable_genre, CFSTR(")"));
+ mutable_genre_length = CFStringGetLength(mutable_genre);
+ if (mutable_genre_length > 0
+ && mutable_genre_length < 4)
+ {
+ if (NULL == number_formatter)
+ {
+ CFLocaleRef locale = CFLocaleCreate(NULL, CFSTR("en_US_POSIX"));
+ number_formatter = CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterDecimalStyle);
+ CFRelease(locale);
+ }
+
+ SInt8 genre_index;
+ CFRange number_range = CFRangeMake(0, mutable_genre_length);
+ if (NULL != number_formatter
+ && false != CFNumberFormatterGetValueFromString(number_formatter,
+ mutable_genre,
+ &number_range,
+ kCFNumberSInt8Type,
+ &genre_index)
+ && number_range.length == mutable_genre_length
+ && number_range.location == 0)
+ {
+
+ if (genre_index >= 0
+ && genre_index < 256
+ && metadata_api)
+ {
+ int ret = metadata_api->GetGenre(genre_index, value);
+ if (ret == NErr_Success)
+ {
+ NXStringRetain(*value);
+ NXStringRelease(genre);
+ }
+ ret = NErr_Success;
+ }
+ }
+ }
+ }
+
+ CFRelease(mutable_genre);
+ return ret;
+#elif defined(__linux__)
+ char *tmp = genre->string;
+ while (*tmp == ' ') tmp++;
+
+ if (!strncmp(tmp, "(RX)", 4))
+ {
+ NXStringRelease(genre);
+ return NXStringCreateWithUTF8(value, "Remix");
+ }
+ else if (!strncmp(tmp, "(CR)", 4))
+ {
+ NXStringRelease(genre);
+ return NXStringCreateWithUTF8(value, "Cover");
+ }
+
+ if (*tmp == '(' || (*tmp >= '0' && *tmp <= '9')) // both (%d) and %d forms
+ {
+ int noparam = 0;
+
+ if (*tmp == '(') tmp++;
+ else noparam = 1;
+ size_t genre_index = atoi(tmp);
+ int cnt = 0;
+ while (*tmp >= '0' && *tmp <= '9') cnt++, tmp++;
+ while (*tmp == ' ') tmp++;
+
+ if (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0)
+ {
+ if (genre_index < 256 && metadata_api)
+ {
+ int ret = metadata_api->GetGenre(genre_index, value);
+ if (ret == NErr_Success)
+ {
+ NXStringRetain(*value);
+ NXStringRelease(genre);
+ return ret;
+ }
+ }
+ }
+ }
+#else
+#error port me!
+#endif
+ }
+ return NErr_Success;
+}
+
+static int ID3v2_GetText(nsid3v2_tag_t id3v2_tag, int frame_enum, unsigned int index, nx_string_t *value)
+{
+ if (!id3v2_tag)
+ return NErr_Empty;
+
+ nsid3v2_frame_t frame;
+ int ret = NSID3v2_Tag_GetFrame(id3v2_tag, frame_enum, &frame);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ return NSID3v2_Frame_Text_Get(frame, value, 0);
+}
+
+static int ID3v2_GetTXXX(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t *value)
+{
+ if (!id3v2_tag)
+ return NErr_Empty;
+
+ nsid3v2_frame_t frame;
+ int ret = NSID3v2_Tag_TXXX_Find(id3v2_tag, description, &frame, 0);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ return NSID3v2_Frame_UserText_Get(frame, 0, value, 0);
+}
+
+static int ID3v2_GetComments(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t *value)
+{
+ if (!id3v2_tag)
+ return NErr_Empty;
+
+ nsid3v2_frame_t frame;
+ int ret = NSID3v2_Tag_Comments_Find(id3v2_tag, description, &frame, 0);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ return NSID3v2_Frame_Comments_Get(frame, 0, 0, value, 0);
+}
+
+// only one of value1 or value2 should be non-NULL
+static int SplitSlash(nx_string_t track, nx_string_t *value1, nx_string_t *value2)
+{
+ char track_utf8[64];
+ size_t bytes_copied;
+ int ret;
+ ret = NXStringGetBytes(&bytes_copied, track, track_utf8, 64, nx_charset_utf8, nx_string_get_bytes_size_null_terminate);
+ if (ret == NErr_Success)
+ {
+ size_t len = strcspn(track_utf8, "/");
+
+ if (value2)
+ {
+ const char *second = &track_utf8[len];
+ if (*second)
+ second++;
+
+ if (!*second)
+ return NErr_Empty;
+
+ return NXStringCreateWithUTF8(value2, second);
+ }
+ else
+ {
+ if (len == 0)
+ return NErr_Empty;
+ return NXStringCreateWithBytes(value1, track_utf8, len, nx_charset_utf8);
+ }
+
+ return NErr_Success;
+ }
+
+ return ret;
+}
+
+/* ifc_metadata implementation */
+int ID3v2Metadata::Metadata_GetField(int field, unsigned int index, nx_string_t *value)
+{
+ if (!id3v2_tag)
+ return NErr_Unknown;
+
+ int ret;
+
+ switch (field)
+ {
+ case MetadataKeys::ARTIST:
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_LEADARTIST, index, value);
+
+ case MetadataKeys::ALBUM_ARTIST:
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_BAND, index, value); /* Windows Media Player style */
+ if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
+ return ret;
+
+ ret = ID3v2_GetTXXX(id3v2_tag, "ALBUM ARTIST", index, value); /* foobar 2000 style */
+ if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
+ return ret;
+
+ ret = ID3v2_GetTXXX(id3v2_tag, "ALBUMARTIST", index, value); /* mp3tag style */
+ if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
+ return ret;
+
+ return ID3v2_GetTXXX(id3v2_tag, "Band", index, value); /* audacity style */
+
+ case MetadataKeys::ALBUM:
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_ALBUM, index, value);
+
+ case MetadataKeys::TITLE:
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TITLE, index, value);
+
+ case MetadataKeys::GENRE:
+ return GetGenre(index, value);
+
+ case MetadataKeys::TRACK:
+ {
+ ReferenceCountedNXString track;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
+ if (ret == NErr_Success)
+ return SplitSlash(track, value, 0);
+
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::TRACKS:
+{
+ ReferenceCountedNXString track;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
+ if (ret == NErr_Success)
+ return SplitSlash(track, 0, value);
+
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::YEAR:
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, value);
+ if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
+ return ret;
+
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_YEAR, index, value);
+
+ case MetadataKeys::DISC:
+ {
+ ReferenceCountedNXString track;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
+ if (ret == NErr_Success)
+ return SplitSlash(track, value, 0);
+
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::DISCS:
+ {
+ ReferenceCountedNXString track;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
+ if (ret == NErr_Success)
+ return SplitSlash(track, 0, value);
+
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::COMPOSER:
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_COMPOSER, index, value);
+
+ case MetadataKeys::PUBLISHER:
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PUBLISHER, index, value);
+
+ case MetadataKeys::BPM:
+ return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_BPM, index, value);
+
+ case MetadataKeys::COMMENT:
+ return ID3v2_GetComments(id3v2_tag, "", index, value);
+ // TODO case MetadataKeys::PLAY_COUNT:
+ // TODO case MetadataKeys::RATING:
+
+ case MetadataKeys::TRACK_GAIN:
+ return ID3v2_GetTXXX(id3v2_tag, "replaygain_track_gain", index, value);
+
+ case MetadataKeys::TRACK_PEAK:
+ return ID3v2_GetTXXX(id3v2_tag, "replaygain_track_peak", index, value);
+
+ case MetadataKeys::ALBUM_GAIN:
+ return ID3v2_GetTXXX(id3v2_tag, "replaygain_album_gain", index, value);
+
+ case MetadataKeys::ALBUM_PEAK:
+ return ID3v2_GetTXXX(id3v2_tag, "replaygain_album_peak", index, value);
+ }
+
+ return NErr_Unknown;
+}
+
+static int IncSafe(const char *&value, size_t &value_length, size_t increment_length)
+{
+ /* eat leading spaces */
+ while (*value == ' ' && value_length)
+ {
+ value++;
+ value_length--;
+ }
+
+ if (increment_length > value_length)
+ return NErr_NeedMoreData;
+
+ value += increment_length;
+ value_length -= increment_length;
+ /* eat trailing spaces */
+ while (*value == ' ' && value_length)
+ {
+ value++;
+ value_length--;
+ }
+
+ return NErr_Success;
+}
+
+static int SplitSlashInteger(nx_string_t track, unsigned int *value1, unsigned int *value2)
+{
+ char track_utf8[64];
+ size_t bytes_copied;
+ int ret;
+ ret = NXStringGetBytes(&bytes_copied, track, track_utf8, 64, nx_charset_utf8, nx_string_get_bytes_size_null_terminate);
+ if (ret == NErr_Success)
+ {
+ size_t len = strcspn(track_utf8, "/");
+
+ if (track_utf8[len])
+ *value2 = strtoul(&track_utf8[len+1], 0, 10);
+ else
+ *value2 = 0;
+
+ track_utf8[len]=0;
+ *value1 = strtoul(track_utf8, 0, 10);
+
+ return NErr_Success;
+ }
+
+ return ret;
+}
+
+int ID3v2Metadata::Metadata_GetInteger(int field, unsigned int index, int64_t *value)
+{
+ if (!id3v2_tag)
+ return NErr_Unknown;
+
+ switch(field)
+ {
+ case MetadataKeys::TRACK:
+ {
+ ReferenceCountedNXString track;
+ int ret;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
+ if (ret == NErr_Success)
+ {
+ unsigned int itrack, itracks;
+ ret = SplitSlashInteger(track, &itrack, &itracks);
+ if (ret == NErr_Success)
+ {
+ if (itrack == 0)
+ return NErr_Empty;
+
+ *value = itrack;
+ return NErr_Success;
+
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::TRACKS:
+ {
+ ReferenceCountedNXString track;
+ int ret;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
+ if (ret == NErr_Success)
+ {
+ unsigned int itrack, itracks;
+ ret = SplitSlashInteger(track, &itrack, &itracks);
+ if (ret == NErr_Success)
+ {
+ if (itracks == 0)
+ return NErr_Empty;
+
+ *value = itracks;
+ return NErr_Success;
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::DISC:
+ {
+ ReferenceCountedNXString track;
+ int ret;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
+ if (ret == NErr_Success)
+ {
+ unsigned int idisc, idiscs;
+ ret = SplitSlashInteger(track, &idisc, &idiscs);
+ if (ret == NErr_Success)
+ {
+ if (idisc == 0)
+ return NErr_Empty;
+
+ *value = idisc;
+ return NErr_Success;
+
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::DISCS:
+ {
+ ReferenceCountedNXString track;
+ int ret;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
+ if (ret == NErr_Success)
+ {
+ unsigned int idisc, idiscs;
+ ret = SplitSlashInteger(track, &idisc, &idiscs);
+ if (ret == NErr_Success)
+ {
+ if (idiscs == 0)
+ return NErr_Empty;
+
+ *value = idiscs;
+ return NErr_Success;
+
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case MetadataKeys::BPM:
+ {
+ ReferenceCountedNXString bpm;
+ int ret;
+ ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_BPM, index, &bpm);
+ if (ret == NErr_Success)
+ {
+ /* TODO: benski> implement NXStringGetInt64Value */
+ int value32;
+ ret = NXStringGetIntegerValue(bpm, &value32);
+ if (ret != NErr_Success)
+ return ret;
+ *value = value32;
+ return NErr_Success;
+ }
+ return ret;
+ }
+ case MetadataKeys::PREGAP:
+ {
+ ReferenceCountedNXString str;
+ char language[3];
+ int ret = NSID3v2_Tag_Comments_Get(id3v2_tag, "iTunSMPB", language, &str, 0);
+ if (ret == NErr_Success)
+ {
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ const char *itunsmpb;
+ size_t itunsmpb_length;
+ char temp[64] = {0};
+ if (NXStringGetCString(str, temp, sizeof(temp)/sizeof(*temp), &itunsmpb, &itunsmpb_length) == NErr_Success)
+ {
+ /* skip first set of meaningless values */
+ if (IncSafe(itunsmpb, itunsmpb_length, 8) == NErr_Success && itunsmpb_length >= 8)
+ {
+ /* read pre-gap */
+ *value = strtoul(itunsmpb, 0, 16);
+ return NErr_Success;
+ }
+ }
+ return NErr_Error;
+ }
+ else
+ return ret;
+
+ }
+ case MetadataKeys::POSTGAP:
+ {
+ ReferenceCountedNXString str;
+ char language[3];
+ int ret = NSID3v2_Tag_Comments_Get(id3v2_tag, "iTunSMPB", language, &str, 0);
+ if (ret == NErr_Success)
+ {
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ const char *itunsmpb;
+ size_t itunsmpb_length;
+ char temp[64] = {0};
+ if (NXStringGetCString(str, temp, sizeof(temp)/sizeof(*temp), &itunsmpb, &itunsmpb_length) == NErr_Success)
+ {
+ /* two separate calls so we can skip spaces properly */
+ if (IncSafe(itunsmpb, itunsmpb_length, 8) == NErr_Success && itunsmpb_length >= 8
+ && IncSafe(itunsmpb, itunsmpb_length, 8) == NErr_Success && itunsmpb_length >= 8)
+ {
+ *value = strtoul(itunsmpb, 0, 16);
+ return NErr_Success;
+ }
+
+ }
+ return NErr_Error;
+ }
+ else
+ return ret;
+
+ }
+ }
+ return NErr_Unknown;
+}
+
+int ID3v2Metadata::Metadata_GetReal(int field, unsigned int index, double *value)
+{
+ if (!id3v2_tag)
+ return NErr_Unknown;
+
+ int ret;
+ nx_string_t str;
+ switch (field)
+ {
+ case MetadataKeys::TRACK_GAIN:
+ ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_track_gain", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ case MetadataKeys::TRACK_PEAK:
+ ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_track_peak", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ case MetadataKeys::ALBUM_GAIN:
+ ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_album_gain", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ case MetadataKeys::ALBUM_PEAK:
+ ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_album_peak", index, &str);
+ if (ret == NErr_Success)
+ {
+ ret = NXStringGetDoubleValue(str, value);
+ NXStringRelease(str);
+ }
+ return ret;
+ }
+ return NErr_Unknown;
+}
+
+static int ArtLookupType(uint8_t *id3v2_type, int metadata_key)
+{
+ switch(metadata_key)
+ {
+ case MetadataKeys::ALBUM:
+ *id3v2_type = 3;
+ return NErr_Success;
+ }
+ return NErr_Unknown;
+}
+
+static int NXStringCreateWithMIME(nx_string_t *mime_type, nx_string_t in)
+{
+ if (!mime_type)
+ return NErr_Success;
+
+ char temp[128];
+ size_t copied;
+ int ret = NXStringGetBytes(&copied, in, temp, 128, nx_charset_ascii, nx_string_get_bytes_size_null_terminate);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (strstr(temp, "/") != 0)
+ {
+ *mime_type = NXStringRetain(in);
+ return NErr_Success;
+ }
+ else
+ {
+ char temp2[128];
+#ifdef _WIN32
+ _snprintf(temp2, 127, "image/%s", temp);
+#else
+ snprintf(temp2, 127, "image/%s", temp);
+#endif
+ temp2[127]=0;
+
+ return NXStringCreateWithUTF8(mime_type, temp2);
+ }
+}
+
+int ID3v2Metadata::Metadata_GetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags)
+{
+ if (!id3v2_tag)
+ return NErr_Unknown;
+
+ uint8_t id3v2_picture_type;
+ int ret = ArtLookupType(&id3v2_picture_type, field);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (!id3v2_tag)
+ return NErr_Empty;
+
+ bool found_one=false;
+ nsid3v2_frame_t frame=0;
+ ret = NSID3v2_Tag_GetFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, &frame);
+ if (ret != NErr_Success)
+ return ret;
+
+ for (;;)
+ {
+ uint8_t this_type;
+ if (NSID3v2_Frame_Picture_Get(frame, 0, &this_type, 0, 0, 0, 0) == NErr_Success && (this_type == id3v2_picture_type || (id3v2_picture_type == 3 && this_type == 0)))
+ {
+ found_one=true;
+ if (index == 0)
+ {
+
+ if (artwork)
+ {
+ nx_data_t data=0;
+
+ if (flags != DATA_FLAG_NONE)
+ {
+ const void *picture_data;
+ size_t picture_length;
+ ReferenceCountedNXString mime_local, description;
+
+ ret = NSID3v2_Frame_Picture_Get(frame, TestFlag(flags, DATA_FLAG_MIME)?(&mime_local):0, &this_type, TestFlag(flags, DATA_FLAG_DESCRIPTION)?(&description):0, &picture_data, &picture_length, 0);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (TestFlag(flags, DATA_FLAG_DATA))
+ {
+ ret = NXDataCreate(&data, picture_data, picture_length);
+ if (ret != NErr_Success)
+ return ret;
+ }
+ else
+ {
+ ret = NXDataCreateEmpty(&data);
+ if (ret != NErr_Success)
+ return ret;
+ }
+
+ if (mime_local)
+ {
+ ReferenceCountedNXString mime_type;
+ ret = NXStringCreateWithMIME(&mime_type, mime_local);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ NXDataSetMIME(data, mime_type);
+ }
+
+ if (description)
+ {
+ NXDataSetDescription(data, description);
+ }
+ }
+ artwork->data = data;
+ /* id3v2 doesn't store height and width, so zero these */
+ artwork->width=0;
+ artwork->height=0;
+ }
+ return NErr_Success;
+ }
+ else
+ {
+ index--; // keep looking
+ }
+ }
+
+ if (NSID3v2_Tag_GetNextFrame(id3v2_tag, frame, &frame) != NErr_Success)
+ {
+ if (found_one)
+ return NErr_EndOfEnumeration;
+ else
+ return NErr_Empty;
+ }
+ }
+}
+
+static int SetText(nsid3v2_tag_t id3v2_tag, int frame_id, unsigned int index, nx_string_t value)
+{
+ if (index > 0)
+ return NErr_Success;
+
+ if (!value)
+ {
+ nsid3v2_frame_t frame;
+ if (NSID3v2_Tag_GetFrame(id3v2_tag, frame_id, &frame) == NErr_Success)
+ {
+ for(;;)
+ {
+ nsid3v2_frame_t next;
+ int ret = NSID3v2_Tag_GetNextFrame(id3v2_tag, frame, &next);
+ NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
+ if (ret != NErr_Success)
+ break;
+ frame=next;
+ }
+ }
+ return NErr_Success;
+ }
+ else
+ {
+ return NSID3v2_Tag_Text_Set(id3v2_tag, frame_id, value, 0);
+ }
+}
+
+static int SetTXXX(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t value, int text_flags)
+{
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ if (!value)
+ {
+ nsid3v2_frame_t frame;
+ for(;;)
+ {
+ if (NSID3v2_Tag_TXXX_Find(id3v2_tag, description, &frame, text_flags) == NErr_Success)
+ NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
+ else
+ return NErr_Success;
+ }
+ }
+ else
+ {
+ return NSID3v2_Tag_TXXX_Set(id3v2_tag, description, value, 0);
+ }
+}
+
+static int SetComments(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t value, int text_flags)
+{
+ if (index > 0)
+ return NErr_EndOfEnumeration;
+
+ if (!value)
+ {
+ nsid3v2_frame_t frame;
+ for(;;)
+ {
+ if (NSID3v2_Tag_Comments_Find(id3v2_tag, description, &frame, text_flags) == NErr_Success)
+ NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
+ else
+ return NErr_Success;
+ }
+ }
+ else
+ {
+ return NSID3v2_Tag_Comments_Set(id3v2_tag, description, "\0\0\0", value, 0);
+ }
+}
+
+int ID3v2Metadata::MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)
+{
+ int ret;
+
+ switch (field)
+ {
+ case MetadataKeys::ARTIST:
+ return SetText(id3v2_tag, NSID3V2_FRAME_LEADARTIST, index, value);
+
+ case MetadataKeys::ALBUM_ARTIST:
+ ret = SetText(id3v2_tag, NSID3V2_FRAME_BAND, index, value);
+ /* delete some of the alternates */
+ SetTXXX(id3v2_tag, "ALBUM ARTIST", index, 0, 0); /* foobar 2000 style */
+ SetTXXX(id3v2_tag, "ALBUMARTIST", index, 0, 0); /* mp3tag style */
+
+ if (!value) /* this might be a valid field, so only delete it if we're specifically deleting album artist (because otherwise, if it's here it's going to get picked up by GetField */
+ SetTXXX(id3v2_tag, "Band", index, 0, 0); /* audacity style */
+
+ return ret;
+
+ case MetadataKeys::ALBUM:
+ return SetText(id3v2_tag, NSID3V2_FRAME_ALBUM, index, value);
+
+ case MetadataKeys::TITLE:
+ return SetText(id3v2_tag, NSID3V2_FRAME_TITLE, index, value);
+
+ case MetadataKeys::GENRE:
+ return SetText(id3v2_tag, NSID3V2_FRAME_CONTENTTYPE, index, value);
+
+ case MetadataKeys::YEAR:
+ /* try to set "newer" style TDRC, first */
+ ret = SetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, value);
+ if (ret == NErr_Success)
+ {
+ /* if it succeeded, remove the older TYER tag */
+ SetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, 0);
+ return ret;
+ }
+
+ /* fall back to using TYER */
+ return SetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, 0);
+
+ case MetadataKeys::TRACK:
+ return SetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, value);
+
+ case MetadataKeys::DISC:
+ return SetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, value);
+
+ case MetadataKeys::COMPOSER:
+ return SetText(id3v2_tag, NSID3V2_FRAME_COMPOSER, index, value);
+
+ case MetadataKeys::PUBLISHER:
+ return SetText(id3v2_tag, NSID3V2_FRAME_PUBLISHER, index, value);
+
+ case MetadataKeys::BPM:
+ return SetText(id3v2_tag, NSID3V2_FRAME_BPM, index, value);
+
+ case MetadataKeys::COMMENT:
+ return SetComments(id3v2_tag, "", index, value, 0);
+
+ case MetadataKeys::TRACK_GAIN:
+ return SetTXXX(id3v2_tag, "replaygain_track_gain", index, value, 0);
+
+ case MetadataKeys::TRACK_PEAK:
+ return SetTXXX(id3v2_tag, "replaygain_track_peak", index, value, 0);
+
+ case MetadataKeys::ALBUM_GAIN:
+ return SetTXXX(id3v2_tag, "replaygain_album_gain", index, value, 0);
+
+ case MetadataKeys::ALBUM_PEAK:
+ return SetTXXX(id3v2_tag, "replaygain_album_peak", index, value, 0);
+ }
+
+ return NErr_Unknown;
+}
+
+static int ID3v2_SetPicture(nsid3v2_frame_t frame, uint8_t id3v2_picture_type, artwork_t *artwork, data_flags_t flags)
+{
+ int ret;
+
+ const void *picture_data;
+ size_t picture_length;
+ ret = NXDataGet(artwork->data, &picture_data, &picture_length);
+ if (ret != NErr_Success)
+ return ret;
+ ReferenceCountedNXString mime_type, description;
+ if (TestFlag(flags, DATA_FLAG_MIME))
+ NXDataGetMIME(artwork->data, &mime_type);
+ if (TestFlag(flags, DATA_FLAG_DESCRIPTION))
+ NXDataGetDescription(artwork->data, &description);
+ return NSID3v2_Frame_Picture_Set(frame, mime_type, id3v2_picture_type, description, picture_data, picture_length, 0);
+}
+
+int ID3v2Metadata::MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags)
+{
+ uint8_t id3v2_picture_type;
+ int ret = ArtLookupType(&id3v2_picture_type, field);
+ if (ret != NErr_Success)
+ return ret;
+
+
+ nsid3v2_frame_t frame=0;
+ ret = NSID3v2_Tag_GetFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, &frame);
+ if (ret != NErr_Success)
+ {
+ if (artwork && artwork->data)
+ {
+ /* create a new one and store */
+ int ret = NSID3v2_Tag_CreateFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, 0, &frame);
+ if (ret == NErr_Success)
+ {
+ ret = ID3v2_SetPicture(frame, id3v2_picture_type, artwork, flags);
+ if (ret == NErr_Success)
+ {
+ ret = NSID3v2_Tag_AddFrame(id3v2_tag, frame);
+ if (ret != NErr_Success)
+ {
+ NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
+ }
+ }
+ }
+ return ret;
+ }
+ else
+ return NErr_Success;
+ }
+
+ for (;;)
+ {
+ /* iterate now, because we might delete the current frame */
+ nsid3v2_frame_t next_frame=0;
+ if (NSID3v2_Tag_GetNextFrame(id3v2_tag, frame, &next_frame) != NErr_Success)
+ next_frame=0; /* just in case */
+
+ uint8_t this_type;
+ if (NSID3v2_Frame_Picture_Get(frame, 0, &this_type, 0, 0, 0, 0) == NErr_Success && (this_type == id3v2_picture_type || (id3v2_picture_type == 3 && this_type == 0)))
+ {
+ if (index == 0)
+ {
+ if (artwork && artwork->data)
+ {
+ return ID3v2_SetPicture(frame, id3v2_picture_type, artwork, flags);
+ }
+ else
+ {
+ NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
+ }
+ }
+ else
+ {
+ index--; // keep looking
+ }
+ }
+
+ if (!next_frame)
+ {
+ if (!artwork || !artwork->data)
+ return NErr_Success;
+ else
+ {
+ /* create a new one and store */
+ int ret = NSID3v2_Tag_CreateFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, 0, &frame);
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = ID3v2_SetPicture(frame, id3v2_picture_type, artwork, flags);
+ if (ret != NErr_Success)
+ return ret;
+ ret = NSID3v2_Tag_AddFrame(id3v2_tag, frame);
+ if (ret != NErr_Success)
+ {
+ NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
+ }
+ return ret;
+ }
+ }
+ frame = next_frame;
+ }
+
+ return NErr_NotImplemented;
+}
diff --git a/Src/replicant/nswasabi/ID3v2Metadata.h b/Src/replicant/nswasabi/ID3v2Metadata.h
new file mode 100644
index 000000000..9fdf23b37
--- /dev/null
+++ b/Src/replicant/nswasabi/ID3v2Metadata.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "metadata/metadata.h"
+#include "nsid3v2/nsid3v2.h"
+
+/* this class mimics ifc_metadata and ifc_metadata_editor, but doesn't inherit (because it's not given out directly) */
+class ID3v2Metadata
+{
+public:
+ ID3v2Metadata();
+ ~ID3v2Metadata();
+
+ static int Initialize(api_metadata *metadata_api);
+ int Initialize(nsid3v2_tag_t tag);
+
+ /* ifc_metadata implementation */
+ 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);
+
+ /* ifc_metadata_editor implementation */
+ int WASABICALL MetadataEditor_SetField(int field, unsigned int index, nx_string_t value);
+ int WASABICALL MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags);
+private:
+ nsid3v2_tag_t id3v2_tag;
+
+ int GetGenre(int index, nx_string_t *value);
+
+ static api_metadata *metadata_api;
+
+#ifdef __APPLE__
+ CFNumberFormatterRef number_formatter;
+#endif
+}; \ No newline at end of file
diff --git a/Src/replicant/nswasabi/Makefile b/Src/replicant/nswasabi/Makefile
new file mode 100644
index 000000000..2a44c7084
--- /dev/null
+++ b/Src/replicant/nswasabi/Makefile
@@ -0,0 +1,48 @@
+MODULE_NAME := nswasabi
+
+CPPSOURCES := ComponentManagerBase.cpp ApplicationBase.cpp ID3v2Metadata.cpp ID3v1Metadata.cpp PlaybackBase.cpp APEv2Metadata.cpp
+#CSOURCES := utf.c ByteReader.c ByteWriter.c
+
+LIBRARY_FILENAME := lib$(MODULE_NAME).a
+OUTPUT_PATH := ../build/$(MODULE_NAME)
+ARCHIVE_PATH := ../build/lib
+LIBRARY_FILEPATH := ../build/lib/$(LIBRARY_FILENAME)
+
+CPPOBJS := $(patsubst %.cpp,$(OUTPUT_PATH)/%.o,$(CPPSOURCES))
+CPPDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(CPPOBJS))
+COBJS := $(patsubst %.c,$(OUTPUT_PATH)/%.o,$(CSOURCES))
+CDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(COBJS))
+
+OBJS := $(CPPOBJS) $(COBJS)
+DEPS := $(CPPDEPS) $(CDEPS)
+
+CFLAGS=-I.. -fPIC #-fvisibility=hidden
+CPPFLAGS := ${CFLAGS}
+
+
+
+build: build-dir $(LIBRARY_FILEPATH)
+
+build-dir:
+ @mkdir -p $(OUTPUT_PATH)/linux > /dev/null 2> /dev/null
+ @mkdir -p $(OUTPUT_PATH) > /dev/null 2> /dev/null
+ @mkdir -p $(ARCHIVE_PATH) > /dev/null 2> /dev/null
+
+dep:
+ @rm ${DEPS}
+
+$(OUTPUT_PATH)/%.o: %.cpp
+ #@echo Compiling $*.cpp
+ @$(CXX) $(CPPFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.cpp -o $(OUTPUT_PATH)/$*.o
+
+$(OUTPUT_PATH)/%.o: %.c
+ #@echo Compiling $*.c
+ @$(CC) $(CFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.c -o $(OUTPUT_PATH)/$*.o
+
+$(LIBRARY_FILEPATH): ${OBJS}
+ @$(AR) rcs $@ ${OBJS}
+
+clean:
+ -rm -f ${OBJS} $(LIBRARY_FILENAME) ${DEPS}
+
+-include $(DEPS)
diff --git a/Src/replicant/nswasabi/MetadataChain.h b/Src/replicant/nswasabi/MetadataChain.h
new file mode 100644
index 000000000..cc6de698b
--- /dev/null
+++ b/Src/replicant/nswasabi/MetadataChain.h
@@ -0,0 +1,97 @@
+#pragma once
+#include "metadata/ifc_metadata.h"
+
+
+template <class metadata_t>
+class MetadataChain : public metadata_t
+{
+public:
+ MetadataChain()
+ {
+ parent_metadata=0;
+ }
+
+ ~MetadataChain()
+ {
+ if (parent_metadata)
+ parent_metadata->Release();
+ }
+
+ static bool Unhandled(ns_error_t ret)
+ {
+ return (ret == NErr_NotImplemented || ret == NErr_Empty || ret == NErr_Unknown);
+ }
+
+ ns_error_t SetParentMetadata(ifc_metadata *new_parent_metadata)
+ {
+ if (parent_metadata)
+ parent_metadata->Release();
+ parent_metadata=new_parent_metadata;
+ if (parent_metadata)
+ parent_metadata->Retain();
+ return NErr_Success;
+ }
+protected:
+ ifc_metadata *parent_metadata;
+private:
+ ns_error_t WASABICALL Metadata_GetField(int field, unsigned int index, nx_string_t *value)
+ {
+ ns_error_t ret = metadata_t::Metadata_GetField(field, index, value);
+ if (Unhandled(ret) && parent_metadata)
+ return parent_metadata->GetField(field, index, value);
+ else
+ return ret;
+
+ }
+
+ ns_error_t WASABICALL Metadata_GetInteger(int field, unsigned int index, int64_t *value)
+ {
+ ns_error_t ret = metadata_t::Metadata_GetInteger(field, index, value);
+ if (Unhandled(ret) && parent_metadata)
+ return parent_metadata->GetInteger(field, index, value);
+ else
+ return ret;
+
+ }
+
+ ns_error_t WASABICALL Metadata_GetReal(int field, unsigned int index, double *value)
+ {
+ ns_error_t ret = metadata_t::Metadata_GetReal(field, index, value);
+ if (Unhandled(ret) && parent_metadata)
+ return parent_metadata->GetReal(field, index, value);
+ else
+ return ret;
+
+ }
+
+ ns_error_t WASABICALL Metadata_GetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags)
+ {
+ ns_error_t ret = metadata_t::Metadata_GetArtwork(field, index, artwork, flags);
+ if (Unhandled(ret) && parent_metadata)
+ return parent_metadata->GetArtwork(field, index, artwork, flags);
+ else
+ return ret;
+
+ }
+
+ ns_error_t WASABICALL Metadata_GetBinary(int field, unsigned int index, nx_data_t *data)
+ {
+ ns_error_t ret = metadata_t::Metadata_GetBinary(field, index, data);
+ if (Unhandled(ret) && parent_metadata)
+ return parent_metadata->GetBinary(field, index, data);
+ else
+ return ret;
+
+ }
+
+ ns_error_t WASABICALL Metadata_GetMetadata(int field, unsigned int index, ifc_metadata **metadata)
+ {
+ ns_error_t ret = metadata_t::Metadata_GetMetadata(field, index, metadata);
+ if (Unhandled(ret) && parent_metadata)
+ return parent_metadata->GetMetadata(field, index, metadata);
+ else
+ return ret;
+
+ }
+ // TODO: ns_error_t WASABICALL Metadata_Serialize(nx_data_t *data) { return NErr_NotImplemented; }
+};
diff --git a/Src/replicant/nswasabi/MetadataEditorChain.h b/Src/replicant/nswasabi/MetadataEditorChain.h
new file mode 100644
index 000000000..7142acca5
--- /dev/null
+++ b/Src/replicant/nswasabi/MetadataEditorChain.h
@@ -0,0 +1,100 @@
+#pragma once
+#include "metadata/ifc_metadata_editor.h"
+
+
+template <class metadata_t>
+class MetadataEditorChain : public metadata_t
+{
+public:
+ MetadataEditorChain()
+ {
+ parent_metadata=0;
+ }
+
+ ~MetadataEditorChain()
+ {
+ if (parent_metadata)
+ parent_metadata->Release();
+ }
+
+ static bool Unhandled(ns_error_t ret)
+ {
+ return (ret == NErr_NotImplemented || ret == NErr_Empty || ret == NErr_Unknown);
+ }
+
+ ns_error_t SetParentMetadata(ifc_metadata_editor *new_parent_metadata)
+ {
+ if (parent_metadata)
+ parent_metadata->Release();
+ parent_metadata=new_parent_metadata;
+ if (parent_metadata)
+ parent_metadata->Retain();
+ return NErr_Success;
+ }
+protected:
+ ifc_metadata_editor *parent_metadata;
+private:
+ int WASABICALL MetadataEditor_Save()
+ {
+ if (parent_metadata)
+ return parent_metadata->Save();
+ else
+ return NErr_NotImplemented;
+ }
+
+ int WASABICALL MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)
+ {
+ bool known=false;
+ if (parent_metadata && parent_metadata->SetField(field, index, value) == NErr_Success)
+ known=true;
+ if (metadata_t::MetadataEditor_SetField(field, index, value) == NErr_Success)
+ known=true;
+
+ if (known)
+ return NErr_Success;
+ else
+ return NErr_Unknown;
+ }
+
+ int WASABICALL MetadataEditor_SetInteger(int field, unsigned int index, int64_t value)
+ {
+ bool known=false;
+ if (parent_metadata && parent_metadata->SetInteger(field, index, value) == NErr_Success)
+ known=true;
+ if (metadata_t::MetadataEditor_SetInteger(field, index, value) == NErr_Success)
+ known=true;
+
+ if (known)
+ return NErr_Success;
+ else
+ return NErr_Unknown;
+ }
+
+ int WASABICALL MetadataEditor_SetReal(int field, unsigned int index, double value)
+ {
+ bool known=false;
+ if (parent_metadata && parent_metadata->SetReal(field, index, value) == NErr_Success)
+ known=true;
+ if (metadata_t::MetadataEditor_SetReal(field, index, value) == NErr_Success)
+ known=true;
+
+ if (known)
+ return NErr_Success;
+ else
+ return NErr_Unknown;
+ }
+
+ int WASABICALL MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *data, data_flags_t flags)
+ {
+ bool known=false;
+ if (parent_metadata && parent_metadata->SetArtwork(field, index, data, flags) == NErr_Success)
+ known=true;
+ if (metadata_t::MetadataEditor_SetArtwork(field, index, data, flags) == NErr_Success)
+ known=true;
+
+ if (known)
+ return NErr_Success;
+ else
+ return NErr_Unknown;
+ }
+}; \ No newline at end of file
diff --git a/Src/replicant/nswasabi/ObjectFactory.h b/Src/replicant/nswasabi/ObjectFactory.h
new file mode 100644
index 000000000..55a998207
--- /dev/null
+++ b/Src/replicant/nswasabi/ObjectFactory.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "service/ifc_servicefactory.h"
+#include "ReferenceCounted.h"
+/*
+====== Usage ======
+disp_t: your Dispatchable base class
+implt_t: your implementation class
+
+ObjectFactory<disp_t, impl_t> myFactory;
+impl_t myImplementation;
+
+//....
+
+//during service registration
+myFactory.Register(WASABI2_API_SVC);
+
+//during service deregistration
+myFactory.Deregister(WASABI2_API_SVC);
+
+==== Class requirements ====
+your base or implementation class requires the following three static methods
+static FOURCC getServiceType(); // return your type (e.g. WaSvc::OBJECT)... might already be defined in the dispatchable base class
+static const char *getServiceName(); // return your service name
+static GUID getServiceGuid(); // return your service GUID
+*/
+
+template <class impl_t, class disp_t>
+class CountableObjectFactory : public ifc_serviceFactory
+{
+public:
+ CountableObjectFactory()
+ {
+ }
+
+ ~CountableObjectFactory()
+ {
+ }
+
+ void Register(api_service *serviceManager)
+ {
+ serviceManager->Register(this);
+ }
+
+ void Deregister(api_service *serviceManager)
+ {
+ serviceManager->Unregister(this);
+ }
+
+private:
+ GUID WASABICALL ServiceFactory_GetServiceType() { return impl_t::GetServiceType(); }
+ nx_string_t WASABICALL ServiceFactory_GetServiceName() { return impl_t::GetServiceName(); }
+ GUID WASABICALL ServiceFactory_GetGUID() { return impl_t::GetServiceGUID(); } // GUID per service factory, can be INVALID_GUID
+ void *WASABICALL ServiceFactory_GetInterface() { return static_cast<disp_t *>(new ReferenceCounted<impl_t>); }
+ int WASABICALL ServiceFactory_ServiceNotify(int msg, intptr_t param1 = 0, intptr_t param2 = 0) { return 0; }
+};
+
diff --git a/Src/replicant/nswasabi/PlaybackBase.cpp b/Src/replicant/nswasabi/PlaybackBase.cpp
new file mode 100644
index 000000000..d3a94a6e8
--- /dev/null
+++ b/Src/replicant/nswasabi/PlaybackBase.cpp
@@ -0,0 +1,409 @@
+#include "PlaybackBase.h"
+#include <stdlib.h>
+#include <assert.h>
+#ifdef __ANDROID__
+#include <android/log.h> // TODO: replace with generic logging API
+
+#else
+#define ANDROID_LOG_INFO 0
+#define ANDROID_LOG_ERROR 1
+static void __android_log_print(int, const char *, const char *, ...)
+{
+}
+#endif
+PlaybackBase::PlaybackBase()
+{
+ wake_flags=0;
+ last_wake_flags=0;
+ playback_thread=0;
+ secondary_parameters=0;
+ filename=0;
+ player=0;
+ queued_seek=0;
+ output_service=0;
+}
+
+int PlaybackBase::Initialize(nx_uri_t filename, ifc_player *player)
+{
+ this->player = player;
+ this->filename = NXURIRetain(filename);
+ return NErr_Success;
+}
+
+PlaybackBase::~PlaybackBase()
+{
+ if (secondary_parameters)
+ secondary_parameters->Release();
+ if (filename)
+ NXURIRelease(filename);
+ if (queued_seek)
+ free(queued_seek);
+ if (playback_thread)
+ NXThreadJoin(playback_thread, 0);
+}
+
+int PlaybackBase::Playback_Play(svc_output *output, ifc_playback_parameters *secondary_parameters)
+{
+ if (!playback_thread)
+ return 1;
+
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Play");
+
+ output_service = output;
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ this->secondary_parameters = secondary_parameters;
+ if (secondary_parameters)
+ secondary_parameters->Retain();
+
+ apc->func = APC_Play;
+ apc->param1 = this;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+int PlaybackBase::Playback_SeekSeconds(double seconds)
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Seek (%f seconds)", seconds);
+ Agave_Seek *seek = (Agave_Seek *)malloc(sizeof(Agave_Seek));
+ if (seek)
+ {
+ seek->position_type = AGAVE_PLAYPOSITION_SECONDS;
+ seek->position.seconds = seconds;
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ apc->func = APC_Seek;
+ apc->param1 = this;
+ apc->param2 = seek;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ }
+ free(seek);
+ return NErr_OutOfMemory;
+
+}
+
+int PlaybackBase::Playback_Pause()
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Pause");
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ apc->func = APC_Pause;
+ apc->param1 = this;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+int PlaybackBase::Playback_Unpause()
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Unpause");
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ apc->func = APC_Unpause;
+ apc->param1 = this;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+int PlaybackBase::Playback_Stop()
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Stop");
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ apc->func = APC_Stop;
+ apc->param1 = this;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+int PlaybackBase::Playback_Close()
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Close");
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ apc->func = APC_Close;
+ apc->param1 = this;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+
+int PlaybackBase::FileLockCallback_Interrupt()
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] Interrupt");
+ threadloop_node_t *apc = thread_loop.GetAPC();
+ if (apc)
+ {
+ apc->func = APC_Interrupt;
+ apc->param1 = this;
+ thread_loop.Schedule(apc);
+ return NErr_Success;
+ }
+ else
+ return NErr_OutOfMemory;
+}
+
+void PlaybackBase::ToggleFlags(int wake_reason)
+{
+ switch(wake_reason)
+ {
+ case WAKE_KILL:
+ last_wake_flags ^= WAKE_KILL; /* toggle kill flag */
+ break;
+ case WAKE_STOP:
+ last_wake_flags ^= WAKE_STOP; /* toggle stop flag */
+ break;
+ case WAKE_PLAY:
+ last_wake_flags ^= WAKE_PLAY; /* toggle play flag */
+ break;
+ case WAKE_PAUSE:
+ case WAKE_UNPAUSE:
+ last_wake_flags ^= WAKE_PAUSE; /* toggle pause flag */
+ break;
+ case WAKE_INTERRUPT:
+ last_wake_flags ^= WAKE_INTERRUPT; /* toggle interrupt flag */
+ break;
+ }
+}
+
+int PlaybackBase::WakeReason(int mask) const
+{
+ int reason_awoken = last_wake_flags ^ wake_flags;
+
+ reason_awoken = reason_awoken & mask;
+
+ if (reason_awoken & WAKE_INTERRUPT)
+ {
+ if (wake_flags & WAKE_INTERRUPT)
+ return WAKE_INTERRUPT;
+ else
+ return WAKE_RESUME;
+ }
+
+ if (reason_awoken & WAKE_STOP)
+ {
+ return WAKE_STOP;
+ }
+
+ if (reason_awoken & WAKE_KILL)
+ {
+ return WAKE_KILL;
+ }
+
+ if (reason_awoken & WAKE_PLAY)
+ {
+ if (wake_flags & WAKE_PLAY)
+ return WAKE_PLAY;
+ else /* if someone cleared the play flag for whatever reason, just treat it as a 0 */
+ return 0;
+ }
+
+ if (reason_awoken & WAKE_PAUSE)
+ {
+ if (wake_flags & WAKE_PAUSE)
+ return WAKE_PAUSE;
+ else
+ return WAKE_UNPAUSE;
+ }
+
+ return 0;
+}
+
+int PlaybackBase::Wake(int mask)
+{
+ assert(mask != 0); /* make sure they didn't specify a 0 mask (which would make this function potentially never return */
+ assert((mask & WAKE_ALL_MASK) != 0); /* make sure it's a valid mask */
+
+ for (;;)
+ {
+ int reason_awoken = last_wake_flags ^ wake_flags;
+ reason_awoken = reason_awoken & mask;
+
+ if (reason_awoken)
+ {
+ int ret = WakeReason(mask);
+ ToggleFlags(ret); // mark the last-known-state of the wake flags
+
+ return ret;
+
+ }
+
+ if (((mask & WAKE_PLAY) && !(wake_flags & WAKE_PLAY))/* if we're stopped and they asked to be woken up for play */
+ || ((mask & WAKE_PAUSE) && (wake_flags & WAKE_PAUSE)) /* or waiting to be woken up for unpause */
+ || ((mask & WAKE_INTERRUPT) && (wake_flags & WAKE_INTERRUPT))) /* or waiting to be woken up for resume */
+ {
+ thread_loop.Step();
+
+ int ret = WakeReason(mask);
+ if (ret) /* if ret is !0, it means we gotten woken up for a reason we care about */
+ {
+ ToggleFlags(ret); // mark the last-known-state of the wake flags
+ return ret;
+ }
+ }
+ else /* no reason to sleep, so just return 0 (no change) */
+ {
+ return 0;
+ }
+ }
+}
+
+int PlaybackBase::Check(int mask)
+{
+ assert(mask != 0); /* make sure they didn't specify a 0 mask (which would make this function potentially never return */
+ assert((mask & WAKE_ALL_MASK) != 0); /* make sure it's a valid mask */
+
+ int reason_awoken = last_wake_flags ^ wake_flags;
+ reason_awoken = reason_awoken & mask;
+
+ int ret = 0;
+ if (reason_awoken)
+ {
+ ret = WakeReason(mask);
+ ToggleFlags(ret); // mark the last-known-state of the wake flags
+ }
+ return ret;
+}
+
+
+int PlaybackBase::Wait(unsigned int milliseconds, int mask)
+{
+ int reason_awoken = last_wake_flags ^ wake_flags;
+ reason_awoken = reason_awoken & mask;
+
+ if (reason_awoken)
+ {
+ int ret = WakeReason(mask);
+ ToggleFlags(ret); // mark the last-known-state of the wake flags
+
+ return ret;
+ }
+
+ thread_loop.Step(milliseconds);
+
+ int ret = WakeReason(mask);
+ ToggleFlags(ret); // mark the last-known-state of the wake flags
+ return ret;
+}
+
+int PlaybackBase::Sleep(unsigned int milliseconds, int mask)
+{
+ int reason_awoken = last_wake_flags ^ wake_flags;
+ reason_awoken = reason_awoken & mask;
+
+ if (reason_awoken)
+ {
+ int ret = WakeReason(mask);
+ assert(ret != 0);
+
+ return ret;
+
+ }
+
+ thread_loop.Step(milliseconds);
+
+ int ret = WakeReason(mask);
+ return ret;
+
+}
+
+void PlaybackBase::OnStopPlaying()
+{
+ // turn off the play flag (also adjust old wake flags so we don't trigger a WAKE_STOP)
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] OnStopPlaying");
+
+ wake_flags &= ~WAKE_PLAY;
+ last_wake_flags &= ~WAKE_PLAY;
+}
+
+void PlaybackBase::OnInterrupted()
+{
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "[PlaybackBase] OnInterrupted");
+
+ wake_flags &= ~WAKE_INTERRUPT;
+ last_wake_flags &= ~WAKE_INTERRUPT;
+}
+
+Agave_Seek *PlaybackBase::GetSeek()
+{
+ Agave_Seek *seek = queued_seek;
+ queued_seek=0;
+ return seek;
+}
+
+void PlaybackBase::FreeSeek(Agave_Seek *seek)
+{
+ free(seek);
+}
+
+bool PlaybackBase::PendingSeek()
+{
+ return !!queued_seek;
+}
+
+void PlaybackBase::APC_Play(void *_playback_base, void *param2, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ playback_base->wake_flags |= WAKE_PLAY;
+}
+
+void PlaybackBase::APC_Seek(void *_playback_base, void *_seek, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ Agave_Seek *seek = (Agave_Seek *)_seek;
+ free(playback_base->queued_seek);
+ playback_base->queued_seek = seek;
+
+}
+
+void PlaybackBase::APC_Pause(void *_playback_base, void *param2, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ playback_base->wake_flags |= WAKE_PAUSE;
+}
+
+void PlaybackBase::APC_Unpause(void *_playback_base, void *param2, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ playback_base->wake_flags &= ~WAKE_PAUSE;
+}
+
+void PlaybackBase::APC_Stop(void *_playback_base, void *param2, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ playback_base->wake_flags |= WAKE_STOP;
+}
+
+void PlaybackBase::APC_Close(void *_playback_base, void *param2, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ playback_base->wake_flags |= WAKE_KILL;
+}
+
+void PlaybackBase::APC_Interrupt(void *_playback_base, void *param2, double real_value)
+{
+ PlaybackBase *playback_base = (PlaybackBase *)_playback_base;
+ playback_base->wake_flags |= WAKE_INTERRUPT;
+}
diff --git a/Src/replicant/nswasabi/PlaybackBase.h b/Src/replicant/nswasabi/PlaybackBase.h
new file mode 100644
index 000000000..8a27387ca
--- /dev/null
+++ b/Src/replicant/nswasabi/PlaybackBase.h
@@ -0,0 +1,91 @@
+#pragma once
+#include "nx/nx.h"
+#include "nu/LockFreeItem.h"
+#include "player/ifc_playback.h"
+#include "player/ifc_player.h"
+#include "player/svc_output.h"
+#include "nu/ThreadLoop.h"
+#include "filelock/api_filelock.h"
+
+/* TODO: we can probably redo this without the mutex, possibly using semaphores */
+class PlaybackBase : public ifc_playback, public cb_filelock
+{
+public:
+ using ifc_playback::Retain;
+ using ifc_playback::Release;
+
+
+ /* ifc_playback implementation */
+ int WASABICALL Playback_Play(svc_output *output, ifc_playback_parameters *secondary_parameters);
+ int WASABICALL Playback_SeekSeconds(double seconds);
+ int WASABICALL Playback_Pause();
+ int WASABICALL Playback_Unpause();
+ int WASABICALL Playback_Stop();
+ int WASABICALL Playback_Close();
+
+ /* cb_filelock implementation */
+ int WASABICALL FileLockCallback_Interrupt();
+protected:
+ nx_thread_t playback_thread;
+ svc_output *output_service;
+ ifc_player *player;
+ ifc_playback_parameters *secondary_parameters;
+ nx_uri_t filename;
+
+ enum
+ {
+ WAKE_KILL=(1<<0),
+ WAKE_PLAY=(1<<1),
+ WAKE_PAUSE=(1<<2),
+ WAKE_STOP=(1<<3),
+ WAKE_INTERRUPT=(1<<4),
+ WAKE_UNPAUSE=(1<<5), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason
+ WAKE_RESUME=(1<<6), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason
+ WAKE_START_MASK = WAKE_PLAY|WAKE_STOP,
+ WAKE_KILL_MASK = WAKE_KILL|WAKE_STOP,
+ WAKE_ALL_MASK = WAKE_KILL|WAKE_PLAY|WAKE_PAUSE|WAKE_STOP|WAKE_INTERRUPT,
+ };
+
+
+protected:
+ PlaybackBase();
+ ~PlaybackBase();
+
+ /* === API for derived classes to use === */
+ int Initialize(nx_uri_t filename, ifc_player *player);
+ int Init();
+
+ /* this checks if one of the flags in mask has toggled.
+ if playback is stopped and WAKE_PLAY is in the mask, this will sleep until a flag changes
+ if playback is paused and WAKE_PAUSE is in the mask, this will sleep until a flag changes
+ if both WAKE_PLAY and WAKE_PAUSE are in the mask, this will sleep until either happens
+ returns 0 if no flag changed */
+ int Wake(int mask); /* Sleeps indefinitely until a flag changes */
+ int Check(int mask); /* like Wake() but never actually goes to sleep for any reason */
+ int Wait(unsigned int milliseconds, int mask); /* Sleeps for a limited amount of time for a flag to change */
+ int Sleep(unsigned int milliseconds, int mask); /* unlike Wait, this one does *not* update last flags */
+ int WakeReason(int mask) const;
+
+ void OnInterrupted(); /* turn off the WAKE_INTERRUPT flag */
+ void OnStopPlaying(); /* turn off the WAKE_PLAY flag */
+ bool PendingSeek();
+ Agave_Seek *GetSeek();
+ void FreeSeek(Agave_Seek *seek);
+ /* === End of API for derived classes to use === */
+private:
+ void ToggleFlags(int wake_reason);
+
+ int wake_flags; /* marked volatile so the compiler doesn't cache */
+ int last_wake_flags;
+ Agave_Seek *queued_seek;
+
+ ThreadLoop thread_loop;
+ static void APC_Play(void *_playback_base, void *param2, double real_value);
+ static void APC_Seek(void *_playback_base, void *param2, double real_value);
+ static void APC_Pause(void *_playback_base, void *param2, double real_value);
+ static void APC_Unpause(void *_playback_base, void *param2, double real_value);
+ static void APC_Stop(void *_playback_base, void *param2, double real_value);
+ static void APC_Close(void *_playback_base, void *param2, double real_value);
+ static void APC_Interrupt(void *_playback_base, void *param2, double real_value);
+
+};
diff --git a/Src/replicant/nswasabi/PlaybackBase2.cpp b/Src/replicant/nswasabi/PlaybackBase2.cpp
new file mode 100644
index 000000000..4a924e23f
--- /dev/null
+++ b/Src/replicant/nswasabi/PlaybackBase2.cpp
@@ -0,0 +1,490 @@
+#include "PlaybackBase2.h"
+
+PlaybackImpl::PlaybackImpl()
+{
+ playback=0;
+ playback_parameters=0;
+}
+
+PlaybackImpl::~PlaybackImpl()
+{
+ // TODO: decide if we need playback->Release() or not
+ if (playback_parameters)
+ playback_parameters->Release();
+}
+
+void PlaybackImpl::Connect(PlaybackBase2 *playback, ifc_playback_parameters *playback_parameters)
+{
+ this->playback = playback;
+ // TODO: decide if we need playback->Retain() or not
+
+ this->playback_parameters = playback_parameters;
+ if (playback_parameters)
+ playback_parameters->Retain();
+
+}
+
+/* ---------- */
+PlaybackBase2::PlaybackBase2()
+{
+ out=0;
+ implementation=0;
+ filelocker=0;
+ paused=false;
+ last_position=0;
+ output_pointers=0;
+ exact_length=false;
+ memset(&parameters, 0, sizeof(parameters));
+}
+
+PlaybackBase2::~PlaybackBase2()
+{
+ /* out should have hopefully been close already. just in case */
+ if (out)
+ out->Release();
+ out=0;
+
+ if (filelocker)
+ filelocker->Release();
+ delete implementation;
+ free(output_pointers);
+
+}
+
+ns_error_t PlaybackBase2::Initialize(api_service *service_manager, PlaybackImpl *implementation, nx_uri_t filename, ifc_player *player)
+{
+ service_manager->GetService(&filelocker);
+
+ this->implementation = implementation;
+ ns_error_t ret = PlaybackBase::Initialize(filename, player);
+ if (ret != NErr_Success)
+ return ret;
+
+ implementation->Connect(this, secondary_parameters);
+
+ this->ifc_playback::Retain(); /* the thread needs to hold a reference to this object so that it doesn't disappear out from under us */
+ NXThreadCreate(&playback_thread, PlayerThreadFunction, this);
+ return NErr_Success;
+}
+
+nx_thread_return_t PlaybackBase2::PlayerThreadFunction(nx_thread_parameter_t param)
+{
+ PlaybackBase2 *playback = (PlaybackBase2 *)param;
+ NXThreadCurrentSetPriority(NX_THREAD_PRIORITY_PLAYBACK);
+ nx_thread_return_t ret = playback->DecodeLoop();
+ playback->ifc_playback::Release(); /* give up the reference that was acquired before spawning the thread */
+ return ret;
+}
+
+int PlaybackBase2::Init()
+{
+ if (filelocker)
+ filelocker->WaitForReadInterruptable(filename, this);
+
+ ns_error_t ret = implementation->Open(filename);
+ if (ret != NErr_Success)
+ return ret;
+
+ ifc_metadata *metadata;
+ if (implementation->GetMetadata(&metadata) == NErr_Success)
+ {
+ player->SetMetadata(metadata);
+ metadata->Release();
+ }
+ else
+ player->SetMetadata(0);
+
+ player->SetSeekable(implementation->IsSeekable()?1:0);
+
+ double length;
+ ret = implementation->GetLength(&length, &exact_length);
+ if (ret == NErr_Success)
+ player->SetLength(length);
+
+ return NErr_Success;
+}
+
+nx_thread_return_t PlaybackBase2::DecodeLoop()
+{
+ player->OnLoaded(filename);
+
+ int ret = Init();
+ if (ret != NErr_Success)
+ {
+ implementation->Close();
+ if (filelocker)
+ filelocker->UnlockFile(filename);
+ player->OnError(ret);
+ return 0;
+ }
+
+ player->OnReady();
+
+ /* wait for Play (or Stop to abort) */
+ for (;;)
+ {
+ ns_error_t ret = Wake(WAKE_PLAY|WAKE_STOP|WAKE_INTERRUPT);
+ if (ret == WAKE_PLAY)
+ {
+ break;
+ }
+ else if (ret == WAKE_STOP)
+ {
+ player->OnStopped();
+ goto cleanup;
+ }
+ else if (ret == WAKE_INTERRUPT)
+ {
+ ns_error_t ret = Internal_Interrupt();
+ if (ret != NErr_Success)
+ {
+ implementation->Close();
+ player->OnError(ret);
+ goto cleanup;
+ }
+ }
+ }
+
+ /* at this point, we know that PLAY is on */
+ for (;;)
+ {
+ int ret = Check(WAKE_STOP|WAKE_PAUSE|WAKE_INTERRUPT);
+ if (ret == WAKE_PAUSE)
+ {
+ if (out)
+ out->Pause(1);
+ paused=true;
+ continue; /* continue in case there's another wake reason */
+ }
+ else if (ret== WAKE_UNPAUSE)
+ {
+ if (out)
+ out->Pause(0);
+ paused=false;
+ continue; /* continue in case there's another wake reason */
+ }
+ else if (ret == WAKE_STOP)
+ {
+ if (out)
+ {
+ out->Stop();
+ out->Release();
+ out=0;
+ }
+ player->OnStopped();
+ goto cleanup;
+ }
+ else if (ret == WAKE_INTERRUPT)
+ {
+ ns_error_t ret = Internal_Interrupt();
+ if (ret != NErr_Success)
+ {
+ implementation->Close();
+ player->OnError(ret);
+ goto cleanup;
+ }
+ continue;
+ }
+
+ Agave_Seek *seek = PlaybackBase::GetSeek();
+ if (seek)
+ {
+ ns_error_t seek_error;
+ double new_position;
+ ns_error_t ret = implementation->Seek(seek, &seek_error, &new_position);
+ if (ret != NErr_Success)
+ {
+ player->OnError(ret);
+ goto cleanup;
+ }
+ if (out)
+ out->Flush(new_position);
+ player->OnSeekComplete(seek_error, new_position);
+ PlaybackBase::FreeSeek(seek);
+ }
+
+ ret = implementation->DecodeStep();
+ if (ret == NErr_EndOfFile)
+ {
+ if (out)
+ out->Done();
+
+ PlaybackBase::OnStopPlaying();
+ player->OnEndOfFile();
+
+ ret = WaitForClose();
+ if (out)
+ out->Release();
+ out=0;
+
+ if (ret != NErr_True)
+ goto cleanup;
+ }
+ else if (ret != NErr_Success)
+ {
+ if (out)
+ {
+ out->Done();
+ out->Release();
+ out=0;
+ }
+ if (ret != NErr_False)
+ player->OnError(NErr_Error); // TODO: find better error code
+ goto cleanup;
+ }
+ else
+ {
+ if (!exact_length)
+ {
+ double length;
+ ret = implementation->GetLength(&length, &exact_length);
+ if (ret == NErr_Success)
+ player->SetLength(length);
+ }
+ }
+ }
+
+cleanup:
+ implementation->Close();
+
+ if (filelocker)
+ filelocker->UnlockFile(filename);
+ return 0;
+}
+
+ns_error_t PlaybackBase2::WaitForClose()
+{
+ if (!out)
+ {
+ player->OnClosed();
+ return NErr_False;
+ }
+ else for (;;)
+ {
+ int ret = Wait(10, WAKE_PLAY|WAKE_KILL|WAKE_STOP);
+ if (ret == WAKE_KILL)
+ {
+ player->OnClosed();
+ return NErr_False;
+ }
+ else if (ret == WAKE_PLAY)
+ {
+ return NErr_True;
+ }
+ else if (ret == WAKE_STOP)
+ {
+ player->OnStopped();
+ return NErr_False;
+ }
+ else
+ {
+ if (out->Playing() == NErr_True)
+ player->SetPosition(last_position - out->Latency());
+ else
+ {
+ player->SetPosition(last_position);
+ player->OnClosed();
+ return NErr_False;
+ }
+ }
+ }
+}
+
+ns_error_t PlaybackBase2::OpenOutput(const ifc_audioout::Parameters *_parameters)
+{
+ // if out is already set, it means that there was a change in parameters, so we'll start a new stream
+ if (out)
+ {
+ // check to see that the parameters actually changed
+ if (!memcmp(&parameters, _parameters, sizeof(parameters)))
+ return NErr_Success;
+
+ out->Done();
+ out=0;
+ }
+
+ parameters = *_parameters;
+
+ free(output_pointers);
+ output_pointers = (const uint8_t **)malloc(parameters.audio.number_of_channels * sizeof(const uint8_t *));
+ if (!output_pointers)
+ return NErr_OutOfMemory;
+
+ ns_error_t ret = output_service->AudioOpen(&parameters, player, secondary_parameters, &out);
+ if (ret != NErr_Success)
+ {
+ player->OnError(ret);
+ return ret;
+ }
+
+ if (paused)
+ out->Pause(1);
+ else
+ out->Pause(0);
+
+
+ return NErr_True;
+}
+
+int PlaybackBase2::OutputNonInterleaved(const void *decode_buffer, size_t decoded, double start_position)
+{
+ int ret;
+ size_t frames_written=0;
+ const uint8_t **buffer = (const uint8_t **)decode_buffer;
+
+ for (size_t c=0;c<parameters.audio.number_of_channels;c++)
+ {
+ output_pointers[c] = buffer[c];
+ }
+
+ while (decoded)
+ {
+ size_t to_write = out->CanWrite();
+ if (to_write)
+ {
+ if (decoded < to_write)
+ to_write = decoded;
+
+ ret = out->Output(output_pointers, to_write);
+ if (ret != NErr_Success)
+ {
+ out->Release();
+ out=0;
+ return ret;
+ }
+
+ decoded -= to_write;
+ for (size_t c=0;c<parameters.audio.number_of_channels;c++)
+ {
+ output_pointers[c] += to_write/parameters.audio.number_of_channels;
+ }
+ frames_written += to_write/parameters.audio.number_of_channels;
+ player->SetPosition(start_position + (double)frames_written/parameters.audio.sample_rate - out->Latency());
+ }
+ else
+ {
+ ns_error_t ret = OutputWait();
+ if (ret != NErr_Success)
+ return ret;
+ }
+ }
+ return NErr_True;
+}
+
+int PlaybackBase2::Output(const void *decode_buffer, size_t decoded, double start_position)
+{
+ int ret;
+ size_t frames_written=0;
+ const uint8_t *decode8 = (const uint8_t *)decode_buffer;
+ size_t buffer_position=0;
+ while (decoded)
+ {
+ size_t to_write = out->CanWrite();
+ if (to_write)
+ {
+ if (decoded < to_write)
+ to_write = decoded;
+
+ ret = out->Output(&decode8[buffer_position], to_write);
+ if (ret != NErr_Success)
+ {
+ out->Release();
+ out=0;
+ return ret;
+ }
+
+ decoded -= to_write;
+ buffer_position += to_write;
+ frames_written += to_write/parameters.audio.number_of_channels;
+ player->SetPosition(start_position + (double)frames_written/parameters.audio.sample_rate - out->Latency());
+ }
+ else
+ {
+ ns_error_t ret = OutputWait();
+ if (ret != NErr_Success)
+ return ret;
+ }
+ }
+ return NErr_True;
+}
+
+ns_error_t PlaybackBase2::OutputWait()
+{
+ if (paused)
+ {
+ /* if we're paused, we need to sit and wait until we're eiter unpaused or stopped */
+ for (;;)
+ {
+ int ret = Wake(WAKE_STOP|WAKE_PAUSE|WAKE_INTERRUPT);
+ if (ret == WAKE_STOP)
+ {
+ out->Stop();
+ out->Release();
+ out=0;
+ player->OnStopped();
+ return NErr_False;
+ }
+ else if (ret == WAKE_UNPAUSE)
+ {
+ out->Pause(0);
+ paused=false;
+ break;
+ }
+ else if (ret == WAKE_PAUSE)
+ {
+ out->Pause(1);
+ paused=true;
+ }
+ else if (PlaybackBase::PendingSeek())
+ {
+ return NErr_True;
+ }
+ else if (ret == WAKE_INTERRUPT)
+ {
+ ns_error_t ret = Internal_Interrupt();
+ if (ret != NErr_Success)
+ return ret;
+ }
+ }
+ }
+ else
+ {
+ int ret = Wait(10, WAKE_STOP);
+ if (ret == WAKE_STOP)
+ {
+ out->Stop();
+ out->Release();
+ out=0;
+ player->OnStopped();
+ return NErr_False;
+ }
+ }
+ return NErr_Success;
+}
+
+ns_error_t PlaybackBase2::Internal_Interrupt()
+{
+ Agave_Seek resume_information;
+ implementation->Interrupt(&resume_information);
+ ns_error_t ret = filelocker->UnlockFile(filename);
+ if (ret != NErr_Success)
+ {
+ implementation->Close();
+ return ret;
+ }
+ PlaybackBase::OnInterrupted();
+ if (filelocker)
+ filelocker->WaitForReadInterruptable(filename, this);
+ ret = implementation->Resume(&resume_information);
+
+
+ if (ret != NErr_Success)
+ return ret;
+ ifc_metadata *metadata;
+ if (implementation->GetMetadata(&metadata) == NErr_Success)
+ {
+ player->SetMetadata(metadata);
+ metadata->Release();
+ }
+ return NErr_Success;
+} \ No newline at end of file
diff --git a/Src/replicant/nswasabi/PlaybackBase2.h b/Src/replicant/nswasabi/PlaybackBase2.h
new file mode 100644
index 000000000..5743aa53e
--- /dev/null
+++ b/Src/replicant/nswasabi/PlaybackBase2.h
@@ -0,0 +1,78 @@
+#pragma once
+#include "PlaybackBase.h"
+#include "foundation/error.h"
+#include "service/api_service.h"
+/* this one does most of the work for you.
+It assumes blocking I/O and, generally, a simple implementation
+You provide an implementation of PlaybackImpl */
+
+class PlaybackBase2;
+
+class PlaybackImpl
+{
+public:
+ void Connect(PlaybackBase2 *playback, ifc_playback_parameters *playback_parameters);
+
+ /* stuff you need to implement */
+
+ /* destructor. be diligent about closing stuff, can't guarantee that Close() got called beforehand */
+ virtual ~PlaybackImpl();
+
+ virtual ns_error_t Open(nx_uri_t filename)=0;
+ /* you need to handle the possibility that Close gets called more than one time */
+ virtual void Close()=0;
+ virtual bool IsSeekable()=0;
+ /* implementation note: add a reference (Retain) before assigning the value */
+ virtual ns_error_t GetMetadata(ifc_metadata **metadata)=0;
+ /* if you set *exact=false, GetLength will get called after the next DecodeStep */
+ virtual ns_error_t GetLength(double *length, bool *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 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 */
+ virtual ns_error_t 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 Interrupt(Agave_Seek *resume_information)=0;
+ /* During resume, be sure to call player->SetMetadata again */
+ virtual ns_error_t Resume(Agave_Seek *resume_information)=0;
+protected:
+
+ PlaybackBase2 *playback;
+ ifc_playback_parameters *playback_parameters;
+ PlaybackImpl();
+
+};
+
+class PlaybackBase2 : public PlaybackBase
+{
+public:
+ PlaybackBase2();
+ ~PlaybackBase2();
+
+ ns_error_t Initialize(api_service *service_manager, PlaybackImpl *implementation, nx_uri_t filename, ifc_player *player);
+
+ /* this two should ONLY be called from within your DecodeStep function! */
+ ns_error_t OpenOutput(const ifc_audioout::Parameters *parameters); // if this returns an error, return it from DecodeStep()
+ ns_error_t Output(const void *audio_data, size_t audio_data_length, double begin_position_seconds); // if this returns an error, return it from DecodeStep()
+ ns_error_t OutputNonInterleaved(const void *audio_data, size_t audio_data_length, double begin_position_seconds); // if this returns an error, return it from DecodeStep()
+
+private:
+ static nx_thread_return_t NXTHREADCALL PlayerThreadFunction(nx_thread_parameter_t param);
+ nx_thread_return_t NXTHREADCALL DecodeLoop();
+ ns_error_t Init();
+ ns_error_t WaitForClose();
+ ns_error_t OutputWait();
+ ns_error_t Internal_Interrupt();
+
+ PlaybackImpl *implementation;
+ api_filelock *filelocker;
+ ifc_audioout *out;
+ bool paused;
+ double last_position;
+ bool exact_length;
+ ifc_audioout::Parameters parameters;
+ const uint8_t **output_pointers;
+}; \ No newline at end of file
diff --git a/Src/replicant/nswasabi/ReferenceCounted.h b/Src/replicant/nswasabi/ReferenceCounted.h
new file mode 100644
index 000000000..c16b85dc8
--- /dev/null
+++ b/Src/replicant/nswasabi/ReferenceCounted.h
@@ -0,0 +1,232 @@
+#pragma once
+#include "../foundation/dispatch.h"
+#include "../foundation/atomics.h"
+#include <new>
+#include "../nx/nxstring.h"
+#include "../nx/nxuri.h"
+
+#define REFERENCE_COUNT_AS(x) size_t Retain() { return x::Retain(); } size_t Release() { return x::Release(); }
+template <class t>
+class ReferenceCounted : public t
+{
+public:
+ ReferenceCounted() { reference_count = 1; }
+protected:
+ /* Dispatchable implementation */
+
+ size_t WASABICALL Dispatchable_Retain()
+ {
+ return nx_atomic_inc(&reference_count);
+ }
+
+ size_t WASABICALL Dispatchable_Release()
+ {
+ if (!reference_count)
+ return reference_count;
+ size_t r = nx_atomic_dec(&reference_count);
+ if (!r)
+ {
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ delete(this);
+ }
+ return r;
+ }
+ size_t reference_count;
+};
+
+template <class t>
+class ReferenceCountedBase
+{
+public:
+ ReferenceCountedBase() { reference_count = 1; }
+
+ size_t Retain()
+ {
+ return nx_atomic_inc(&reference_count);
+ }
+
+ size_t Release()
+ {
+ if (!reference_count)
+ return reference_count;
+ size_t r = nx_atomic_dec(&reference_count);
+ if (!r)
+ {
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ delete static_cast<t*>(this);
+ }
+ return r;
+ }
+ size_t reference_count;
+};
+
+template <class t>
+class ReferenceCountedObject
+{
+public:
+ ReferenceCountedObject()
+ {
+ ptr = new (std::nothrow) ReferenceCounted<t>;
+ };
+
+ ~ReferenceCountedObject()
+ {
+ if (ptr)
+ ptr->Release();
+ }
+
+ operator t *()
+ {
+ return ptr;
+ }
+
+ t *operator ->()
+ {
+ return ptr;
+ }
+
+ t *ptr;
+};
+
+template <class t>
+class ReferenceCountedPointer
+{
+public:
+ ReferenceCountedPointer()
+ {
+ ptr = 0;
+ };
+
+ ReferenceCountedPointer(t *new_ptr)
+ {
+ ptr = new_ptr;
+ };
+
+ ~ReferenceCountedPointer()
+ {
+ if (ptr)
+ ptr->Release();
+ }
+
+ operator t *()
+ {
+ return ptr;
+ }
+
+ t *operator ->()
+ {
+ return ptr;
+ }
+
+ t **operator &()
+ {
+ // if there's something already in here, we need to release it first
+ if (ptr)
+ ptr->Release();
+ ptr=0;
+ return &ptr;
+ }
+
+ t *operator =(t *new_ptr)
+ {
+ if (ptr)
+ ptr->Release();
+ ptr=0;
+ ptr = new_ptr;
+ return ptr;
+ }
+
+ t *ptr;
+};
+
+class ReferenceCountedNXString
+{
+public:
+ ReferenceCountedNXString()
+ {
+ ptr = 0;
+ }
+
+ ReferenceCountedNXString(const ReferenceCountedNXString &copy)
+ {
+ ptr = NXStringRetain(copy.ptr);
+ }
+
+ ~ReferenceCountedNXString()
+ {
+ NXStringRelease(ptr);
+ }
+
+ operator nx_string_t()
+ {
+ return ptr;
+ }
+
+ nx_string_t *operator &()
+ {
+ // if there's something already in here, we need to release it first
+ if (ptr)
+ NXStringRelease(ptr);
+ ptr=0;
+ return &ptr;
+ }
+
+ nx_string_t operator =(nx_string_t new_ptr)
+ {
+ if (ptr)
+ NXStringRelease(ptr);
+
+ ptr = new_ptr;
+ return ptr;
+ }
+
+ nx_string_t operator ->()
+ {
+ return ptr;
+ }
+
+ nx_string_t ptr;
+};
+
+class ReferenceCountedNXURI
+{
+public:
+ ReferenceCountedNXURI()
+ {
+ ptr = 0;
+ }
+
+ ReferenceCountedNXURI(const ReferenceCountedNXURI &copy)
+ {
+ ptr = NXURIRetain(copy.ptr);
+ }
+
+ ~ReferenceCountedNXURI()
+ {
+ NXURIRelease(ptr);
+ }
+
+ operator nx_uri_t()
+ {
+ return ptr;
+ }
+
+ nx_uri_t *operator &()
+ {
+ // if there's something already in here, we need to release it first
+ if (ptr)
+ NXURIRelease(ptr);
+ ptr=0;
+ return &ptr;
+ }
+
+ nx_uri_t operator ->()
+ {
+ return ptr;
+ }
+ nx_uri_t ptr;
+};
diff --git a/Src/replicant/nswasabi/ServiceName.h b/Src/replicant/nswasabi/ServiceName.h
new file mode 100644
index 000000000..27b1cf2ab
--- /dev/null
+++ b/Src/replicant/nswasabi/ServiceName.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "../nx/nxstring.h"
+
+/*
+this is a helper class to implement GetServiceName
+just put WASABI_SERVICE_NAME("Your Service Name"); in the public section of your class declaration.
+this implementation does leak memory, but I don't think it's that big of a deal (services can't be unloaded at run-time anyway)
+if we ever implement the NXSTR() macro, we can eliminate this leak
+
+e.g.
+class MyAPI : public api_whatever
+{
+public:
+ WASABI_SERVICE_NAME("My API");
+};
+ */
+
+#define WASABI_SERVICE_NAME(x) static nx_string_t GetServiceName(){ static nx_string_t service_name=0; if (!service_name) NXStringCreateWithUTF8(&service_name, x); return NXStringRetain(service_name); }
+#define WASABI_SERVICE_GUID(x) static GUID GetServiceGUID() { return x; }
+
+
diff --git a/Src/replicant/nswasabi/VERSION b/Src/replicant/nswasabi/VERSION
new file mode 100644
index 000000000..ea710abb9
--- /dev/null
+++ b/Src/replicant/nswasabi/VERSION
@@ -0,0 +1 @@
+1.2 \ No newline at end of file
diff --git a/Src/replicant/nswasabi/XMLString.cpp b/Src/replicant/nswasabi/XMLString.cpp
new file mode 100644
index 000000000..4b22afd5b
--- /dev/null
+++ b/Src/replicant/nswasabi/XMLString.cpp
@@ -0,0 +1,44 @@
+#include "XMLString.h"
+
+/* TODO: make and use some sort of nx_mutable_string_t object */
+XMLString::XMLString()
+{
+ data=0;
+}
+
+XMLString::~XMLString()
+{
+ NXMutableStringDestroy(data);
+}
+
+
+void XMLString::Reset()
+{
+ if (data)
+ data->nx_string_data->len = 0; // TODO: make mutable string function for this
+}
+
+nx_string_t XMLString::GetString()
+{
+ // TODO: make mutable string function for this
+ nx_string_t str = data->nx_string_data;
+ data->nx_string_data = 0;
+ NXMutableStringDestroy(data);
+ data=0;
+ return str;
+}
+
+void XMLString::XMLCallback_OnStartElement(const nsxml_char_t *xmlpath, const nsxml_char_t *xmltag, ifc_xmlattributes *attributes)
+{
+ if (data)
+ data->nx_string_data->len = 0; // TODO: make mutable string function for this
+}
+
+void XMLString::XMLCallback_OnCharacterData(const nsxml_char_t *xmlpath, const nsxml_char_t *xmltag, const nsxml_char_t *characters, size_t num_characters)
+{
+ if (!data)
+ data = NXMutableStringCreateFromXML(characters, num_characters);
+ else
+ NXMutableStringGrowFromXML(data, characters, num_characters);
+}
+
diff --git a/Src/replicant/nswasabi/XMLString.h b/Src/replicant/nswasabi/XMLString.h
new file mode 100644
index 000000000..36d1ef9a2
--- /dev/null
+++ b/Src/replicant/nswasabi/XMLString.h
@@ -0,0 +1,21 @@
+#include "xml/ifc_xmlcallback.h"
+#include "nx/nxmutablestring.h"
+
+/* this one is an xml callback that just saves the last encountered string */
+
+class XMLString : public ifc_xmlcallback
+{
+public:
+ XMLString();
+ ~XMLString();
+ void Reset();
+ nx_string_t GetString();
+
+private:
+ /* XML callbacks */
+ void WASABICALL XMLCallback_OnStartElement(const nsxml_char_t *xmlpath, const nsxml_char_t *xmltag, ifc_xmlattributes *attributes);
+ void WASABICALL XMLCallback_OnCharacterData(const nsxml_char_t *xmlpath, const nsxml_char_t *xmltag, const nsxml_char_t *characters, size_t num_characters);
+
+ nx_mutable_string_t data;
+};
+
diff --git a/Src/replicant/nswasabi/nswasabi.sln b/Src/replicant/nswasabi/nswasabi.sln
new file mode 100644
index 000000000..d38f08212
--- /dev/null
+++ b/Src/replicant/nswasabi/nswasabi.sln
@@ -0,0 +1,82 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nswasabi", "nswasabi.vcxproj", "{E4A6C250-B426-4328-945A-303865086F4C}"
+ ProjectSection(ProjectDependencies) = postProject
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27} = {57C90706-B25D-4ACA-9B33-95CDB2427C27}
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}
+ {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D}
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E4A6C250-B426-4328-945A-303865086F4C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E4A6C250-B426-4328-945A-303865086F4C}.Debug|Win32.Build.0 = Debug|Win32
+ {E4A6C250-B426-4328-945A-303865086F4C}.Debug|x64.ActiveCfg = Debug|x64
+ {E4A6C250-B426-4328-945A-303865086F4C}.Debug|x64.Build.0 = Debug|x64
+ {E4A6C250-B426-4328-945A-303865086F4C}.Release|Win32.ActiveCfg = Release|Win32
+ {E4A6C250-B426-4328-945A-303865086F4C}.Release|x64.ActiveCfg = Release|x64
+ {E4A6C250-B426-4328-945A-303865086F4C}.Release|x64.Build.0 = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64
+ {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nswasabi/nswasabi.vcxproj b/Src/replicant/nswasabi/nswasabi.vcxproj
new file mode 100644
index 000000000..d9d330946
--- /dev/null
+++ b/Src/replicant/nswasabi/nswasabi.vcxproj
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{E4A6C250-B426-4328-945A-303865086F4C}</ProjectGuid>
+ <RootNamespace>nswasabi</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x86_Debug\</OutDir>
+ <IntDir>x86_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>x64_Debug\</OutDir>
+ <IntDir>x64_Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x86_Release\</OutDir>
+ <IntDir>x86_Release\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>x64_Release\</OutDir>
+ <IntDir>x64_Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="ApplicationBase.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ApplicationBase.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nswasabi/nswasabi.xcodeproj/project.pbxproj b/Src/replicant/nswasabi/nswasabi.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..bb8b0ca63
--- /dev/null
+++ b/Src/replicant/nswasabi/nswasabi.xcodeproj/project.pbxproj
@@ -0,0 +1,499 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 00479F18151C35C300F99D12 /* nswasabi-prepare */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 00479F19151C35C300F99D12 /* Build configuration list for PBXAggregateTarget "nswasabi-prepare" */;
+ buildPhases = (
+ 00479F20151C3CAE00F99D12 /* Generate Version Info */,
+ );
+ dependencies = (
+ 0039B375152A1F7100D96D3E /* PBXTargetDependency */,
+ );
+ name = "nswasabi-prepare";
+ productName = "nswasabi-version";
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 00479F10151C33FE00F99D12 /* XMLString.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B73470151C308A00A8251C /* XMLString.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00479F22151C3D1D00F99D12 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 00479F21151C3D1D00F99D12 /* version.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B73471151C308A00A8251C /* AutoCharNX.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B7346A151C308A00A8251C /* AutoCharNX.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B73472151C308A00A8251C /* ObjectFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B7346B151C308A00A8251C /* ObjectFactory.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B73473151C308A00A8251C /* ReferenceCounted.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B7346C151C308A00A8251C /* ReferenceCounted.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B73474151C308A00A8251C /* ServiceName.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B7346D151C308A00A8251C /* ServiceName.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B73475151C308A00A8251C /* singleton.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B7346E151C308A00A8251C /* singleton.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00C27E9A15372DEA008D95CD /* precomp.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C27E9915372DEA008D95CD /* precomp.h */; };
+ 0CC9987C14C5DC6900484291 /* PlaybackBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0CC9987A14C5DC6900484291 /* PlaybackBase.cpp */; };
+ 0CC9987D14C5DC6900484291 /* PlaybackBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CC9987B14C5DC6900484291 /* PlaybackBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B104AEA614C7CC5100211271 /* ApplicationBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B104AEA414C7CC5100211271 /* ApplicationBase.cpp */; };
+ B104AEA714C7CC5100211271 /* ApplicationBase.h in Headers */ = {isa = PBXBuildFile; fileRef = B104AEA514C7CC5100211271 /* ApplicationBase.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B15A069D14F499F9004F70E7 /* ID3v1Metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B15A069914F499F9004F70E7 /* ID3v1Metadata.cpp */; };
+ B15A069E14F499F9004F70E7 /* ID3v1Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = B15A069A14F499F9004F70E7 /* ID3v1Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B15A069F14F499F9004F70E7 /* ID3v2Metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B15A069B14F499F9004F70E7 /* ID3v2Metadata.cpp */; };
+ B15A06A014F499F9004F70E7 /* ID3v2Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = B15A069C14F499F9004F70E7 /* ID3v2Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B193DFDA14F49B6F005D99FB /* APEv2Metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B193DFD814F49B6F005D99FB /* APEv2Metadata.cpp */; };
+ B193DFDB14F49B6F005D99FB /* APEv2Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = B193DFD914F49B6F005D99FB /* APEv2Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 0039B374152A1F7100D96D3E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0CC9986414C5DC3C00484291 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00479F14151C35B700F99D12;
+ remoteInfo = "nswasabi-cleanup";
+ };
+ 00479F1C151C3C4000F99D12 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0CC9986414C5DC3C00484291 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00479F14151C35B700F99D12;
+ remoteInfo = "nswasabi-cleanup";
+ };
+ 00479F1E151C3C4300F99D12 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0CC9986414C5DC3C00484291 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00479F18151C35C300F99D12;
+ remoteInfo = "nswasabi-version";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 00479F21151C3D1D00F99D12 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = version.h; path = $PROJECT_DERIVED_FILE_DIR/version.h; sourceTree = "<absolute>"; };
+ 00B7346A151C308A00A8251C /* AutoCharNX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoCharNX.h; sourceTree = "<group>"; };
+ 00B7346B151C308A00A8251C /* ObjectFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectFactory.h; sourceTree = "<group>"; };
+ 00B7346C151C308A00A8251C /* ReferenceCounted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReferenceCounted.h; sourceTree = "<group>"; };
+ 00B7346D151C308A00A8251C /* ServiceName.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServiceName.h; sourceTree = "<group>"; };
+ 00B7346E151C308A00A8251C /* singleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = singleton.h; sourceTree = "<group>"; };
+ 00B7346F151C308A00A8251C /* XMLString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XMLString.cpp; sourceTree = "<group>"; };
+ 00B73470151C308A00A8251C /* XMLString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XMLString.h; sourceTree = "<group>"; };
+ 00B7347A151C309D00A8251C /* VERSION */ = {isa = PBXFileReference; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
+ 00C27E9915372DEA008D95CD /* precomp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = precomp.h; sourceTree = "<group>"; };
+ 0CC9986D14C5DC3C00484291 /* libnswasabi.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnswasabi.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 0CC9987A14C5DC6900484291 /* PlaybackBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlaybackBase.cpp; sourceTree = "<group>"; };
+ 0CC9987B14C5DC6900484291 /* PlaybackBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaybackBase.h; sourceTree = "<group>"; };
+ B104AEA414C7CC5100211271 /* ApplicationBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplicationBase.cpp; sourceTree = "<group>"; };
+ B104AEA514C7CC5100211271 /* ApplicationBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplicationBase.h; sourceTree = "<group>"; };
+ B15A069914F499F9004F70E7 /* ID3v1Metadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ID3v1Metadata.cpp; sourceTree = "<group>"; };
+ B15A069A14F499F9004F70E7 /* ID3v1Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ID3v1Metadata.h; sourceTree = "<group>"; };
+ B15A069B14F499F9004F70E7 /* ID3v2Metadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ID3v2Metadata.cpp; sourceTree = "<group>"; };
+ B15A069C14F499F9004F70E7 /* ID3v2Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ID3v2Metadata.h; sourceTree = "<group>"; };
+ B193DFD814F49B6F005D99FB /* APEv2Metadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = APEv2Metadata.cpp; sourceTree = "<group>"; };
+ B193DFD914F49B6F005D99FB /* APEv2Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APEv2Metadata.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 0CC9986A14C5DC3C00484291 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 00B73479151C308E00A8251C /* Version */ = {
+ isa = PBXGroup;
+ children = (
+ 00B7347A151C309D00A8251C /* VERSION */,
+ 00479F21151C3D1D00F99D12 /* version.h */,
+ );
+ name = Version;
+ sourceTree = "<group>";
+ };
+ 0CC9986214C5DC3C00484291 = {
+ isa = PBXGroup;
+ children = (
+ 00C27E9915372DEA008D95CD /* precomp.h */,
+ B193DFD814F49B6F005D99FB /* APEv2Metadata.cpp */,
+ B193DFD914F49B6F005D99FB /* APEv2Metadata.h */,
+ B104AEA414C7CC5100211271 /* ApplicationBase.cpp */,
+ B104AEA514C7CC5100211271 /* ApplicationBase.h */,
+ 00B7346A151C308A00A8251C /* AutoCharNX.h */,
+ B15A069914F499F9004F70E7 /* ID3v1Metadata.cpp */,
+ B15A069A14F499F9004F70E7 /* ID3v1Metadata.h */,
+ B15A069B14F499F9004F70E7 /* ID3v2Metadata.cpp */,
+ B15A069C14F499F9004F70E7 /* ID3v2Metadata.h */,
+ 00B7346B151C308A00A8251C /* ObjectFactory.h */,
+ 0CC9987A14C5DC6900484291 /* PlaybackBase.cpp */,
+ 0CC9987B14C5DC6900484291 /* PlaybackBase.h */,
+ 00B7346C151C308A00A8251C /* ReferenceCounted.h */,
+ 00B7346D151C308A00A8251C /* ServiceName.h */,
+ 00B7346E151C308A00A8251C /* singleton.h */,
+ 00B7346F151C308A00A8251C /* XMLString.cpp */,
+ 00B73470151C308A00A8251C /* XMLString.h */,
+ 00B73479151C308E00A8251C /* Version */,
+ 0CC9986E14C5DC3C00484291 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 0CC9986E14C5DC3C00484291 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 0CC9986D14C5DC3C00484291 /* libnswasabi.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 0CC9986B14C5DC3C00484291 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0CC9987D14C5DC6900484291 /* PlaybackBase.h in Headers */,
+ B104AEA714C7CC5100211271 /* ApplicationBase.h in Headers */,
+ B15A069E14F499F9004F70E7 /* ID3v1Metadata.h in Headers */,
+ B15A06A014F499F9004F70E7 /* ID3v2Metadata.h in Headers */,
+ B193DFDB14F49B6F005D99FB /* APEv2Metadata.h in Headers */,
+ 00B73471151C308A00A8251C /* AutoCharNX.h in Headers */,
+ 00B73472151C308A00A8251C /* ObjectFactory.h in Headers */,
+ 00B73473151C308A00A8251C /* ReferenceCounted.h in Headers */,
+ 00B73474151C308A00A8251C /* ServiceName.h in Headers */,
+ 00B73475151C308A00A8251C /* singleton.h in Headers */,
+ 00479F10151C33FE00F99D12 /* XMLString.h in Headers */,
+ 00479F22151C3D1D00F99D12 /* version.h in Headers */,
+ 00C27E9A15372DEA008D95CD /* precomp.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXLegacyTarget section */
+ 00479F14151C35B700F99D12 /* nswasabi-cleanup */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(NSBUILD_TOOLS_BIN_DIR)/cleanbuild --xcode-mode --libraries \"$(LIBRARY_PATH)\" \"$(PUBLIC_HEADERS_DIR)\" \"$(DWARF_DSYM_PATH)\" \"$(PROJECT_DERIVED_FILE_DIR)/version.*\"";
+ buildConfigurationList = 00479F15151C35B700F99D12 /* Build configuration list for PBXLegacyTarget "nswasabi-cleanup" */;
+ buildPhases = (
+ );
+ buildToolPath = /bin/sh;
+ buildWorkingDirectory = "";
+ dependencies = (
+ );
+ name = "nswasabi-cleanup";
+ passBuildSettingsInEnvironment = 1;
+ productName = "nswasabi-cleanup";
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ 0CC9986C14C5DC3C00484291 /* nswasabi */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 0CC9987114C5DC3C00484291 /* Build configuration list for PBXNativeTarget "nswasabi" */;
+ buildPhases = (
+ 0CC9986914C5DC3C00484291 /* Sources */,
+ 0CC9986A14C5DC3C00484291 /* Frameworks */,
+ 0CC9986B14C5DC3C00484291 /* Headers */,
+ 00479F13151C354F00F99D12 /* Install Public Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 00479F1D151C3C4000F99D12 /* PBXTargetDependency */,
+ 00479F1F151C3C4300F99D12 /* PBXTargetDependency */,
+ );
+ name = nswasabi;
+ productName = nswasabi;
+ productReference = 0CC9986D14C5DC3C00484291 /* libnswasabi.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0CC9986414C5DC3C00484291 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0500;
+ ORGANIZATIONNAME = "Nullsoft, Inc.";
+ };
+ buildConfigurationList = 0CC9986714C5DC3C00484291 /* Build configuration list for PBXProject "nswasabi" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 0CC9986214C5DC3C00484291;
+ productRefGroup = 0CC9986E14C5DC3C00484291 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 0CC9986C14C5DC3C00484291 /* nswasabi */,
+ 00479F14151C35B700F99D12 /* nswasabi-cleanup */,
+ 00479F18151C35C300F99D12 /* nswasabi-prepare */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00479F13151C354F00F99D12 /* Install Public Headers */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ name = "Install Public Headers";
+ outputPaths = (
+ "$(DSTROOT)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "INSTALLTOOL=\"$NSBUILD_TOOLS_BIN_DIR/installtool\"\n$INSTALLTOOL --headers-only \\\n \"$SCRIPT_INPUT_FILE_0/\" \\\n \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 00479F20151C3CAE00F99D12 /* Generate Version Info */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/VERSION",
+ "$(NSBUILD_TOOLS_SHARE_DIR)/nvgtool/lib-version.template.h",
+ );
+ name = "Generate Version Info";
+ outputPaths = (
+ "$(PROJECT_DERIVED_FILE_DIR)/version.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "PRODUCT_VERSION=$(cat \"$SCRIPT_INPUT_FILE_0\")\n\nif [ ! -d \"$PROJECT_DERIVED_FILE_DIR\" ]; then\n mkdir -p \"$PROJECT_DERIVED_FILE_DIR\"\nfi\n\nNVGTOOL=\"$NSBUILD_TOOLS_BIN_DIR/nvgtool\"\n$NVGTOOL --product-name \"$PRODUCT_NAME\" \\\n --product-version \"$PRODUCT_VERSION\" \\\n --input-file \"$SCRIPT_INPUT_FILE_1\" \\\n --output-file \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 0CC9986914C5DC3C00484291 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0CC9987C14C5DC6900484291 /* PlaybackBase.cpp in Sources */,
+ B104AEA614C7CC5100211271 /* ApplicationBase.cpp in Sources */,
+ B15A069D14F499F9004F70E7 /* ID3v1Metadata.cpp in Sources */,
+ B15A069F14F499F9004F70E7 /* ID3v2Metadata.cpp in Sources */,
+ B193DFDA14F49B6F005D99FB /* APEv2Metadata.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 0039B375152A1F7100D96D3E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00479F14151C35B700F99D12 /* nswasabi-cleanup */;
+ targetProxy = 0039B374152A1F7100D96D3E /* PBXContainerItemProxy */;
+ };
+ 00479F1D151C3C4000F99D12 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00479F14151C35B700F99D12 /* nswasabi-cleanup */;
+ targetProxy = 00479F1C151C3C4000F99D12 /* PBXContainerItemProxy */;
+ };
+ 00479F1F151C3C4300F99D12 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00479F18151C35C300F99D12 /* nswasabi-prepare */;
+ targetProxy = 00479F1E151C3C4300F99D12 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 00479F16151C35B700F99D12 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Debug;
+ };
+ 00479F17151C35B700F99D12 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Release;
+ };
+ 00479F1A151C35C300F99D12 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ 00479F1B151C35C300F99D12 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+ 0CC9986F14C5DC3C00484291 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = a;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACH_O_TYPE = staticlib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ ONLY_ACTIVE_ARCH = YES;
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Debug;
+ };
+ 0CC9987014C5DC3C00484291 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = a;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACH_O_TYPE = staticlib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Release;
+ };
+ 0CC9987214C5DC3C00484291 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ 0CC9987314C5DC3C00484291 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 00479F15151C35B700F99D12 /* Build configuration list for PBXLegacyTarget "nswasabi-cleanup" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00479F16151C35B700F99D12 /* Debug */,
+ 00479F17151C35B700F99D12 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 00479F19151C35C300F99D12 /* Build configuration list for PBXAggregateTarget "nswasabi-prepare" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00479F1A151C35C300F99D12 /* Debug */,
+ 00479F1B151C35C300F99D12 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 0CC9986714C5DC3C00484291 /* Build configuration list for PBXProject "nswasabi" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0CC9986F14C5DC3C00484291 /* Debug */,
+ 0CC9987014C5DC3C00484291 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 0CC9987114C5DC3C00484291 /* Build configuration list for PBXNativeTarget "nswasabi" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0CC9987214C5DC3C00484291 /* Debug */,
+ 0CC9987314C5DC3C00484291 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0CC9986414C5DC3C00484291 /* Project object */;
+}
diff --git a/Src/replicant/nswasabi/nswasabi.xcodeproj/xcshareddata/xcschemes/nswasabi.xcscheme b/Src/replicant/nswasabi/nswasabi.xcodeproj/xcshareddata/xcschemes/nswasabi.xcscheme
new file mode 100644
index 000000000..84c4002b1
--- /dev/null
+++ b/Src/replicant/nswasabi/nswasabi.xcodeproj/xcshareddata/xcschemes/nswasabi.xcscheme
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0500"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "0CC9986C14C5DC3C00484291"
+ BuildableName = "libnswasabi.a"
+ BlueprintName = "nswasabi"
+ ReferencedContainer = "container:nswasabi.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Src/replicant/nswasabi/precomp.h b/Src/replicant/nswasabi/precomp.h
new file mode 100644
index 000000000..955fa4714
--- /dev/null
+++ b/Src/replicant/nswasabi/precomp.h
@@ -0,0 +1,32 @@
+//
+// precomp.h
+// nswasabi
+//
+
+#include <assert.h>
+#include <limits.h>
+#include <new>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include "nu/LockFreeItem.h"
+#include "nu/ThreadLoop.h"
+
+#include "nx/nxcondition.h"
+#include "nx/nxstring.h"
+#include "nx/nxuri.h"
+#include "nx/nxmutablestring.h"
+
+#include "metadata/metadata.h"
+#include "metadata/MetadataKeys.h"
+
+#include "nsid3v1/nsid3v1.h"
+#include "nsid3v2/nsid3v2.h"
+
+#include "service/ifc_servicefactory.h"
diff --git a/Src/replicant/nswasabi/singleton.h b/Src/replicant/nswasabi/singleton.h
new file mode 100644
index 000000000..b31a68bce
--- /dev/null
+++ b/Src/replicant/nswasabi/singleton.h
@@ -0,0 +1,117 @@
+#pragma once
+
+#include "service/ifc_servicefactory.h"
+
+/*
+====== Usage ======
+disp_t: your Dispatchable base class
+implt_t: your implementation class
+
+SingletonServiceFactory<disp_t, impl_t> myFactory;
+impl_t myImplementation;
+
+//....
+
+//during service registration
+myFactory.Register(WASABI2_API_SVC, &myImplementation);
+
+//during service deregistration
+myFactory.Deregister(WASABI2_API_SVC, &myImplementation);
+
+==== Class requirements ====
+your base or implementation class requires the following three static methods
+static FOURCC getServiceType(); // return your type (e.g. WaSvc::UNIQUE)... might already be defined in the dispatchable base class
+static const char *getServiceName(); // return your service name
+static GUID getServiceGuid(); // return your service GUID
+*/
+
+class WasabiServiceFactory
+{
+public:
+ virtual ~WasabiServiceFactory() {}
+ virtual void Register(api_service *serviceManager)=0;
+ virtual void Deregister(api_service *serviceManager)=0;
+};
+
+
+template <class impl_t, class disp_t>
+class SingletonServiceFactory : public ifc_serviceFactory
+{
+public:
+ SingletonServiceFactory() : impl(0)
+ {
+ }
+
+ ~SingletonServiceFactory()
+ {
+ }
+
+ void Register(api_service *serviceManager, impl_t *new_impl)
+ {
+ impl=new_impl;
+ serviceManager->Register(this);
+ }
+
+ void Deregister(api_service *serviceManager)
+ {
+ if (impl)
+ {
+ serviceManager->Unregister(this);
+ impl=0;
+ }
+ }
+
+private:
+ GUID WASABICALL ServiceFactory_GetServiceType() { return impl_t::GetServiceType(); }
+ nx_string_t WASABICALL ServiceFactory_GetServiceName() { return impl_t::GetServiceName(); }
+ GUID WASABICALL ServiceFactory_GetGUID() { return impl_t::GetServiceGUID(); } // GUID per service factory, can be INVALID_GUID
+ void *WASABICALL ServiceFactory_GetInterface() { return static_cast<disp_t *>(impl); }
+ int WASABICALL ServiceFactory_ServiceNotify(int msg, intptr_t param1 = 0, intptr_t param2 = 0) { return 0; }
+
+private:
+ impl_t *impl;
+
+};
+
+/* same as above, but this one also constructs the implementation object itself
+ useful when:
+ 1) you don't need to ever access the implementation object yourself
+ 2) the implementation class requires no constructor parameters
+
+ TODO: might want to change this to "new" the object during Register and "delete" during Deregister,
+ in case destruction during program termination isn't safe.
+*/
+template <class impl_t, class disp_t>
+class SingletonService : public ifc_serviceFactory, public WasabiServiceFactory
+{
+public:
+ SingletonService()
+ {
+ }
+
+ ~SingletonService()
+ {
+ }
+
+ void Register(api_service *serviceManager)
+ {
+ serviceManager->Register(this);
+ }
+
+ void Deregister(api_service *serviceManager)
+ {
+ serviceManager->Unregister(this);
+ }
+
+private:
+ GUID WASABICALL ServiceFactory_GetServiceType() { return impl_t::GetServiceType(); }
+ nx_string_t WASABICALL ServiceFactory_GetServiceName() { return impl_t::GetServiceName(); }
+ GUID WASABICALL ServiceFactory_GetGUID() { return impl_t::GetServiceGUID(); } // GUID per service factory, can be INVALID_GUID
+ void *WASABICALL ServiceFactory_GetInterface() { return static_cast<disp_t *>(&impl); }
+ int WASABICALL ServiceFactory_ServiceNotify(int msg, intptr_t param1 = 0, intptr_t param2 = 0) { return 0; }
+
+private:
+ impl_t impl;
+
+};
+
diff --git a/Src/replicant/nu/AutoBuffer.h b/Src/replicant/nu/AutoBuffer.h
new file mode 100644
index 000000000..a972f327c
--- /dev/null
+++ b/Src/replicant/nu/AutoBuffer.h
@@ -0,0 +1,49 @@
+#pragma once
+#include "foundation/error.h"
+#include <stdlib.h>
+
+/* an automatically growing buffer */
+
+class AutoBuffer
+{
+public:
+ AutoBuffer()
+ {
+ buffer=0;
+ buffer_length=0;
+ }
+
+ ~AutoBuffer()
+ {
+ free(buffer);
+ }
+
+ operator void *()
+ {
+ return buffer;
+ }
+
+ template <class ptr_t>
+ ptr_t Get()
+ {
+ return (ptr_t)buffer;
+ }
+
+ int Reserve(size_t new_size)
+ {
+ if (new_size <= buffer_length)
+ return NErr_Success;
+
+ void *new_buffer = realloc(buffer, new_size);
+ if (!new_buffer)
+ return NErr_OutOfMemory;
+
+ buffer = new_buffer;
+ buffer_length = new_size;
+ return NErr_Success;
+ }
+
+private:
+ void *buffer;
+ size_t buffer_length;
+};
diff --git a/Src/replicant/nu/AutoChar.h b/Src/replicant/nu/AutoChar.h
new file mode 100644
index 000000000..b89bfa316
--- /dev/null
+++ b/Src/replicant/nu/AutoChar.h
@@ -0,0 +1,141 @@
+#ifndef NULLSOFT_AUTOCHARH
+#define NULLSOFT_AUTOCHARH
+#ifdef WIN32
+#include <windows.h>
+#include <stdlib.h>
+
+inline char *AutoCharDupN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
+{
+ if (!convert)
+ return 0;
+ int size = WideCharToMultiByte(codePage, flags, convert, (int)len, 0, 0, NULL, NULL);
+
+ if (!size)
+ return 0;
+
+ char *narrow = (char *)malloc((size+1)*sizeof(char));
+ if ( narrow == 0 )
+ return 0;
+
+ if (!WideCharToMultiByte(codePage, flags, convert, (int)len, narrow, size, NULL, NULL))
+ {
+ free(narrow);
+ narrow=0;
+ }
+ else
+ narrow[size]=0;
+
+ return narrow;
+}
+
+inline char *AutoCharDup(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0)
+{
+ if (!convert)
+ return 0;
+
+ int size = WideCharToMultiByte(codePage, flags, convert, -1, 0, 0, NULL, NULL);
+
+ if (!size)
+ return 0;
+
+ char *narrow = (char *)malloc(size*sizeof(char));
+ if ( narrow == 0 )
+ return 0;
+
+ if (!WideCharToMultiByte(codePage, flags, convert, -1, narrow, size, NULL, NULL))
+ {
+ free(narrow);
+ narrow=0;
+ }
+ return narrow;
+}
+
+class AutoChar
+{
+public:
+ AutoChar(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0) : narrow(0)
+ {
+ narrow = AutoCharDup(convert, codePage, flags);
+ }
+ ~AutoChar()
+ {
+ free(narrow);
+ narrow=0;
+ }
+ operator const char *()
+ {
+ return narrow;
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+protected:
+ AutoChar() : narrow(0)
+ {
+ }
+ char *narrow;
+};
+
+class AutoCharN : public AutoChar
+{
+public:
+ AutoCharN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
+ {
+ narrow = AutoCharDupN(convert, len, codePage, flags);
+ }
+};
+#else
+#include <stdlib.h>
+#include <wchar.h>
+
+inline char *AutoCharDup(const wchar_t *convert)
+{
+ if (!convert)
+ return 0;
+
+ size_t size = wcslen(convert)+1;
+
+ if (!size)
+ return 0;
+
+ char *narrow = (char *)malloc(size*sizeof(char));
+ if ( narrow == 0 )
+ return 0;
+
+ if (wcstombs(narrow, convert, size) == (size_t)-1)
+ {
+ free(narrow);
+ narrow=0;
+ }
+ return narrow;
+}
+
+
+class AutoChar
+{
+public:
+
+ AutoChar(const wchar_t *convert) : narrow(0)
+ {
+ narrow = AutoCharDup(convert);
+ }
+ ~AutoChar()
+ {
+ free(narrow);
+ narrow=0;
+ }
+ operator const char *()
+ {
+ return narrow;
+ }
+ operator char *()
+ {
+ return narrow;
+ }
+private:
+ char *narrow;
+};
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/replicant/nu/AutoLock.h b/Src/replicant/nu/AutoLock.h
new file mode 100644
index 000000000..02370cfce
--- /dev/null
+++ b/Src/replicant/nu/AutoLock.h
@@ -0,0 +1,377 @@
+#ifndef AUTOLOCKH
+#define AUTOLOCKH
+
+#ifdef _WIN32
+#pragma warning (disable:4786)
+#include <windows.h>
+#elif defined(__linux__) || defined(__APPLE__)
+#include <pthread.h>
+#else
+#error port me!!
+#endif
+
+/*
+NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
+this can be VERY useful if you are trying to find a deadlock
+each time the guard is locked or unlocked, it outputs a list of
+any threads using the mutex, and their function stack
+*/
+//#define NULLSOFT_LOCK_OUTPUT_STATS
+
+#ifdef NULLSOFT_LOCK_OUTPUT_STATS
+
+#include <string> // we save each function name as a string
+#include <deque> // we make a list of the recursive function stack for each thread
+#include <map> // and map
+#include <iostream> // we output to std::cerr
+#include <windows.h>
+
+
+/*****
+Description:
+ This class uses scoping to wrap a critical section (lightweight in-process mutex)
+ The constructor enters the mutex and the destructor leaves it. This allows it to
+ take advantage of automatic scoping in C++, because C++ automatically calls the destructor
+ when an object leaves scope.
+
+ This is _especially_ useful when you have multiple return paths, since you don't have to
+ repeat mutex-leaving code.
+
+To use:
+ Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
+ of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
+ to your mutex handle or critical section handle.
+
+ Make an AutoLock object on the stack to lock. It will unlock automatically when the object
+ leaves scope.
+
+ Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
+ unless you have weird requirements and know what you are doing.
+
+ Example:
+
+ class MyClass
+ {
+ LockGuard fileGuard;
+ fstream file;
+ void DumpSomeData() //
+ {
+ AutoLock lock(fileGuard);
+ file << GetData();
+ }
+
+ void CALLBACK NewData() // potentially called by another thread
+ {
+ AutoLock lock(fileGuard)
+ file << newData;
+ }
+ };
+
+
+ Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
+
+ void DoStuff()
+ {
+ a = GetData();
+ { // false scope
+ AutoLock lock(dataGuard);
+ DoCalculationsWith(a);
+ } // mutex will release here
+ SetData(a);
+ }
+
+ Tip: A common mistake is making a temporary object.
+ i.e.
+ CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
+ INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
+
+*******/
+#define MANUALLOCKNAME(x) x
+#define LOCKNAME(x) ,x
+#define GUARDNAME(x) (x)
+namespace Nullsoft
+{
+ namespace Utility
+ {
+ /* the token which represents a resource to be locked */
+ class LockGuard
+ {
+ public:
+ inline LockGuard(const char *name = "Unnamed Guard")
+ {
+ lockName = name;
+ InitializeCriticalSection(&cerr_cs);
+ InitializeCriticalSection(&map_cs);
+ InitializeCriticalSection(&m_cs);
+ }
+
+ inline ~LockGuard()
+ {
+ DeleteCriticalSection(&cerr_cs);
+ DeleteCriticalSection(&map_cs);
+ DeleteCriticalSection(&m_cs);
+ }
+
+ inline void Lock()
+ {
+ EnterCriticalSection(&m_cs);
+ }
+
+ inline void Unlock()
+ {
+ LeaveCriticalSection(&m_cs);
+ }
+
+ int ThreadCount()
+ {
+ EnterCriticalSection(&map_cs);
+ int count = 0;
+ for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
+ {
+ if (!itr->second.empty())
+ count++;
+ }
+
+ LeaveCriticalSection(&map_cs);
+ return count;
+ }
+
+ void Display()
+ {
+ EnterCriticalSection(&map_cs);
+ EnterCriticalSection(&cerr_cs);
+
+ if (ThreadCount() > 1 && owner)
+ {
+ std::cerr << "Guard: " << lockName << std::endl;
+ for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
+ {
+ if (itr->second.empty())
+ continue;
+
+ std::cerr << " Thread ID: " << std::hex << itr->first << std::dec;
+ if (owner == itr->first)
+ std::cerr << " [holding the mutex] *****";
+ else
+ std::cerr << " [blocked]";
+ std::cerr << std::endl;
+ for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
+ {
+ std::cerr << " " << *fitr << "();" << std::endl;
+ }
+ }
+ }
+ LeaveCriticalSection(&cerr_cs);
+ LeaveCriticalSection(&map_cs);
+ }
+
+ void In(DWORD thread, const char *functionName)
+ {
+ EnterCriticalSection(&map_cs);
+ threads[thread].push_back(functionName);
+ LeaveCriticalSection(&map_cs);
+ }
+
+ void Out(DWORD thread)
+ {
+ EnterCriticalSection(&map_cs);
+ threads[thread].pop_back();
+ LeaveCriticalSection(&map_cs);
+ }
+ std::string lockName;
+ CRITICAL_SECTION cerr_cs, map_cs;
+ typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
+ typedef std::map<DWORD, FunctionStack> ThreadMap;
+ ThreadMap threads;
+
+ DWORD owner;
+ private:
+ CRITICAL_SECTION m_cs;
+
+ };
+
+ /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
+ class AutoLock
+ {
+ public:
+ /*
+ @param functionName The function name which wants the mutex
+ we pass it in as a char * even though it'll be converted to a std::string
+ to reduce overhead when OUTPUT_STATS is off
+ */
+ inline AutoLock(LockGuard &_guard, const char *functionName = "function name not passed") : guard(&_guard)
+ {
+ ManualLock(functionName);
+ }
+ inline void ManualLock(char *functionName = "manual lock")
+ {
+ thisThread = GetCurrentThreadId();
+ guard->In(thisThread, functionName);
+ guard->Display();
+ guard->Lock();
+ guard->owner = thisThread;
+ guard->Display();
+ }
+
+ inline void ManualUnlock()
+ {
+ guard->Display();
+ guard->Unlock();
+
+ InterlockedCompareExchange((LONG volatile *)&guard->owner, 0, (LONG)thisThread);
+ /* above line is functionally equivalent to:
+ if (guard->owner == thisThread)
+ guard->owner=0;
+ */
+ guard->Out(thisThread);
+ guard->Display();
+ }
+
+ inline ~AutoLock()
+ {
+ ManualUnlock();
+ }
+
+ LockGuard *guard;
+ DWORD thisThread;
+ };
+
+ }
+}
+
+
+#else
+#define MANUALLOCKNAME(x)
+#define LOCKNAME(x)
+#define GUARDNAME(x)
+namespace nu
+{
+ /* the token which represents a resource to be locked */
+ class LockGuard
+ {
+ public:
+ inline LockGuard(const char *guardName = "")
+ {
+ #ifdef _WIN32
+ InitializeCriticalSection(&m_cs);
+#elif defined(__linux__)
+ pthread_mutexattr_t mtxattr;
+ pthread_mutexattr_init(&mtxattr);
+ pthread_mutexattr_settype(&mtxattr, PTHREAD_MUTEX_RECURSIVE_NP );
+ pthread_mutex_init(&mtx, &mtxattr);
+ pthread_mutexattr_destroy(&mtxattr);
+#elif defined(__APPLE__)
+ pthread_mutexattr_t mtxattr;
+ pthread_mutexattr_init(&mtxattr);
+ pthread_mutexattr_settype(&mtxattr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mtx, &mtxattr);
+ pthread_mutexattr_destroy(&mtxattr);
+ #else
+ #error port me
+ #endif
+ }
+ inline ~LockGuard()
+ {
+ #ifdef _WIN32
+ DeleteCriticalSection(&m_cs);
+ #elif defined(__linux__) || defined(__APPLE__)
+ pthread_mutex_destroy(&mtx);
+ #else
+ #error port me!
+ #endif
+ }
+ inline void Lock()
+ {
+ #ifdef _WIN32
+ EnterCriticalSection(&m_cs);
+ #elif defined(__linux__) || defined(__APPLE__)
+ pthread_mutex_lock(&mtx);
+ #else
+ #error por tme!
+ #endif
+ }
+
+ inline void Unlock()
+ {
+ #ifdef _WIN32
+ LeaveCriticalSection(&m_cs);
+ #elif defined(__linux__) || defined(__APPLE__)
+ pthread_mutex_unlock(&mtx);
+ #else
+ #error port me!
+ #endif
+ }
+ private:
+ #ifdef _WIN32
+ CRITICAL_SECTION m_cs;
+ #elif defined(__linux__) || defined(__APPLE__)
+ pthread_mutex_t mtx;
+ #else
+ #error port me!
+ #endif
+ };
+
+ /* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
+ class AutoLock
+ {
+ public:
+ inline AutoLock(LockGuard &_guard) : guard(&_guard)
+ {
+ guard->Lock();
+ }
+
+ inline AutoLock(LockGuard *_guard) : guard(_guard)
+ {
+ guard->Lock();
+ }
+ inline void ManualLock()
+ {
+ guard->Lock();
+ }
+
+ inline void ManualUnlock()
+ {
+ guard->Unlock();
+
+ }
+ inline ~AutoLock()
+ {
+ guard->Unlock();
+ }
+ LockGuard *guard;
+ };
+
+ // will lock anything that implements Lock() and Unlock()
+ template <class LockGuard_t>
+ class AutoLockT
+ {
+ public:
+ inline AutoLockT(LockGuard_t &_guard) : guard(&_guard)
+ {
+ guard->Lock();
+ }
+
+ inline AutoLockT(LockGuard_t *_guard) : guard(_guard)
+ {
+ guard->Lock();
+ }
+ inline void ManualLock()
+ {
+ guard->Lock();
+ }
+
+ inline void ManualUnlock()
+ {
+ guard->Unlock();
+
+ }
+ inline ~AutoLockT()
+ {
+ guard->Unlock();
+ }
+ LockGuard_t *guard;
+ };
+}
+#endif
+
+#endif
diff --git a/Src/replicant/nu/AutoWide.h b/Src/replicant/nu/AutoWide.h
new file mode 100644
index 000000000..7897aa99a
--- /dev/null
+++ b/Src/replicant/nu/AutoWide.h
@@ -0,0 +1,50 @@
+#ifndef AUTOWIDEH
+#define AUTOWIDEH
+#ifdef WIN32
+#include <windows.h>
+#include <stdlib.h>
+
+inline wchar_t *AutoWideDup(const char *convert, UINT codePage=CP_ACP)
+{
+ if (!convert)
+ return 0;
+
+
+ wchar_t *wide = 0;
+
+ int size = MultiByteToWideChar(codePage, 0, convert, -1, 0,0);
+ if (!size)
+ return 0;
+
+ wide = (wchar_t *)malloc(size*sizeof(wchar_t));
+ if (!MultiByteToWideChar(codePage, 0, convert, -1, wide,size))
+ {
+ free(wide);
+ wide=0;
+ }
+ return wide;
+}
+
+class AutoWide
+{
+public:
+ AutoWide(const char *convert, UINT codePage=CP_ACP) : wide(0)
+ {
+ wide = AutoWideDup(convert, codePage);
+ }
+ ~AutoWide()
+ {
+ free(wide);
+ wide=0;
+ }
+ operator wchar_t *()
+ {
+ return wide;
+ }
+private:
+ wchar_t *wide;
+};
+
+#endif
+
+#endif
diff --git a/Src/replicant/nu/Benchmark.h b/Src/replicant/nu/Benchmark.h
new file mode 100644
index 000000000..3fcd3b0b2
--- /dev/null
+++ b/Src/replicant/nu/Benchmark.h
@@ -0,0 +1,44 @@
+#ifndef BENCHMARKH
+#define BENCHMARKH
+
+
+#if defined(_WIN32)
+static uint64_t Benchmark()
+{
+ return GetTickCount64();
+}
+
+#elif defined(__ANDROID__)
+#include <time.h>
+
+// Make sure to divide by 1000000 (1 million) to get results in Milliseconds, as they are returned in nanoseconds
+static uint64_t Benchmark()
+{
+ struct timespec ts;
+ uint64_t count;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+ count=(uint64_t)ts.tv_sec*1000000000ULL + (uint64_t)ts.tv_nsec;
+ return count;
+}
+
+#elif defined(__APPLE__)
+#include <mach/mach_time.h>
+static uint64_t Benchmark()
+{
+ uint64_t absoluteTime;
+ static mach_timebase_info_data_t timeBase = {0,0};
+
+ absoluteTime = mach_absolute_time();
+
+ if (0 == timeBase.denom)
+ {
+ kern_return_t err = mach_timebase_info(&timeBase);
+ if (0 != err)
+ return 0;
+ }
+ uint64_t nanoTime = absoluteTime * timeBase.numer / timeBase.denom;
+ return nanoTime/(1000*1000);
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/replicant/nu/BitReader.cpp b/Src/replicant/nu/BitReader.cpp
new file mode 100644
index 000000000..7f66f37de
--- /dev/null
+++ b/Src/replicant/nu/BitReader.cpp
@@ -0,0 +1,109 @@
+#include "BitReader.h"
+#include <string.h>
+
+static uint32_t mask[8]=
+{
+ 0x1,
+ 0x3,
+ 0x7,
+ 0xF,
+ 0x1F,
+ 0x3F,
+ 0x7F,
+ 0xFF
+};
+
+static uint32_t msk[33] =
+{
+ 0x00000000,0x00000001,0x00000003,0x00000007,
+ 0x0000000f,0x0000001f,0x0000003f,0x0000007f,
+ 0x000000ff,0x000001ff,0x000003ff,0x000007ff,
+ 0x00000fff,0x00001fff,0x00003fff,0x00007fff,
+ 0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff,
+ 0x000fffff,0x001fffff,0x003fffff,0x007fffff,
+ 0x00ffffff,0x01ffffff,0x03ffffff,0x07ffffff,
+ 0x0fffffff,0x1fffffff,0x3fffffff,0x7fffffff,
+ 0xffffffff
+};
+
+void BitReader::alignbyte()
+{
+ flushbits(numBits&7);
+}
+
+void BitReader::getbytes(void *data, uint32_t n)
+{
+ memcpy(data, this->data, n);
+ flushbits(n*8);
+}
+
+uint8_t BitReader::getbits1()
+{
+ uint8_t byte = data[0];
+ uint32_t count = (numBits-1) & 7;
+ byte &= mask[count];
+ byte >>= count;
+
+ numBits--;
+ if ((numBits % 8) == 0)
+ data++;
+ return byte;
+}
+
+uint32_t BitReader::getbits(uint32_t n)
+{
+ uint32_t val = showbits(n);
+ flushbits(n);
+ return val;
+}
+
+uint8_t BitReader::showbits1() const
+{
+ uint8_t byte = data[0];
+ uint32_t count = (numBits-1) & 7;
+ byte &= mask[count];
+ byte >>= count;
+ return byte;
+}
+
+uint32_t BitReader::showbits(uint32_t n) const
+{
+ uint32_t val;
+ switch((numBits+7) >> 3)
+ {
+ case 0:
+ return 0;
+ case 1:
+ val=(data[0]<<24);
+ break;
+ case 2:
+ val=(data[0]<<24) | (data[1]<<16);
+ break;
+ case 3:
+ val=(data[0]<<24) | (data[1]<<16) | (data[2]<<8);
+ break;
+ default:
+ val=(data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
+ break;
+ }
+ uint32_t c = ((numBits-1) & 7) + 25;
+ return (val>>(c-n)) & msk[n];
+}
+
+void BitReader::flushbits(uint32_t n)
+{
+ uint32_t oldpos = (numBits+7)>>3;
+ numBits-=n;
+ uint32_t newpos = (numBits+7)>>3;
+ data += (oldpos - newpos);
+}
+
+bool BitReader::empty()
+{
+ return numBits==0;
+}
+
+uint32_t BitReader::size() const
+{
+ return numBits;
+}
diff --git a/Src/replicant/nu/BitReader.h b/Src/replicant/nu/BitReader.h
new file mode 100644
index 000000000..8460b30f8
--- /dev/null
+++ b/Src/replicant/nu/BitReader.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "foundation/types.h"
+class BitReader
+{
+public:
+ uint8_t getbits1();
+ uint32_t getbits(uint32_t n);
+ uint8_t showbits1() const;
+ uint32_t showbits(uint32_t n) const;
+ void flushbits(uint32_t n);
+ bool empty();
+ uint32_t size() const; // in bits
+ void alignbyte(); // aligns bitstream to the next byte (or current byte if already aligned)
+ void getbytes(void *data, uint32_t n);
+//private:
+ const uint8_t *data;
+ uint32_t numBits;
+};
diff --git a/Src/replicant/nu/ByteReader.c b/Src/replicant/nu/ByteReader.c
new file mode 100644
index 000000000..762663561
--- /dev/null
+++ b/Src/replicant/nu/ByteReader.c
@@ -0,0 +1,203 @@
+#include "ByteReader.h"
+#include <string.h>
+/* generic LITTLE ENDIAN implementation */
+void bytereader_init(bytereader_t byte_reader, const void *data, size_t byte_length)
+{
+ byte_reader->data = data;
+ byte_reader->byte_length = byte_length;
+ byte_reader->data_ptr = (const uint8_t *)data;
+}
+
+
+void bytereader_advance(bytereader_t byte_reader, size_t bytes)
+{
+ byte_reader->byte_length -= bytes;
+ byte_reader->data_ptr += bytes;
+}
+
+void bytereader_reset(bytereader_t byte_reader)
+{
+ size_t diff = byte_reader->data_ptr - (const uint8_t *)byte_reader->data;
+ byte_reader->byte_length += diff;
+ byte_reader->data_ptr = (const uint8_t *)byte_reader->data;
+}
+
+size_t bytereader_find_zero(bytereader_t byte_reader)
+{
+ size_t i=0;
+
+ for (i=0;i<byte_reader->byte_length && byte_reader->data_ptr[i];i++)
+ {
+ // empty loop
+ }
+ return i;
+}
+
+/* n byte functions */
+void bytereader_show_n(bytereader_t byte_reader, void *destination, size_t bytes)
+{
+ memcpy(destination, byte_reader->data_ptr, bytes);
+}
+
+void bytereader_read_n(bytereader_t byte_reader, void *destination, size_t bytes)
+{
+ memcpy(destination, byte_reader->data_ptr, bytes);
+ bytereader_advance(byte_reader, bytes);
+}
+
+/* 1 byte functions */
+uint8_t bytereader_show_u8(bytereader_t byte_reader)
+{
+ return byte_reader->data_ptr[0];
+}
+
+uint8_t bytereader_read_u8(bytereader_t byte_reader)
+{
+ uint8_t ret = byte_reader->data_ptr[0];
+ bytereader_advance(byte_reader, 1);
+ return ret;
+}
+
+int8_t bytereader_show_s8(bytereader_t byte_reader)
+{
+ return *(const int8_t *)(byte_reader->data_ptr);
+}
+
+int8_t bytereader_read_s8(bytereader_t byte_reader)
+{
+ int8_t ret = *(const int8_t *)(byte_reader->data_ptr);
+ bytereader_advance(byte_reader, 1);
+ return ret;
+}
+
+/* 2 byte little-endian functions */
+
+uint16_t bytereader_show_u16_le(bytereader_t byte_reader)
+{
+ return (uint16_t)byte_reader->data_ptr[0] | ((uint16_t)byte_reader->data_ptr[1] << 8);
+}
+
+uint16_t bytereader_read_u16_le(bytereader_t byte_reader)
+{
+ uint16_t ret = bytereader_show_u16_le(byte_reader);
+ bytereader_advance(byte_reader, 2);
+ return ret;
+}
+
+int16_t bytereader_show_s16_le(bytereader_t byte_reader)
+{
+ return (int16_t)byte_reader->data_ptr[0] | ((int16_t)byte_reader->data_ptr[1] << 8);
+}
+
+int16_t bytereader_read_s16_le(bytereader_t byte_reader)
+{
+ int16_t ret = bytereader_show_s16_le(byte_reader);
+ bytereader_advance(byte_reader, 2);
+ return ret;
+}
+
+/* 2 byte big-endian functions */
+uint16_t bytereader_show_u16_be(bytereader_t byte_reader)
+{
+ return (uint16_t)byte_reader->data_ptr[1] | ((uint16_t)byte_reader->data_ptr[0] << 8);
+}
+
+uint16_t bytereader_read_u16_be(bytereader_t byte_reader)
+{
+ uint16_t ret = bytereader_show_u16_be(byte_reader);
+ bytereader_advance(byte_reader, 2);
+ return ret;
+}
+
+int16_t bytereader_show_s16_be(bytereader_t byte_reader)
+{
+ return (int16_t)byte_reader->data_ptr[1] | ((int16_t)byte_reader->data_ptr[0] << 8);
+}
+
+int16_t bytereader_read_s16_be(bytereader_t byte_reader)
+{
+ int16_t ret = bytereader_show_s16_be(byte_reader);
+ bytereader_advance(byte_reader, 2);
+ return ret;
+}
+
+/* 4 byte functions */
+
+uint32_t bytereader_show_u32_be(bytereader_t byte_reader)
+{
+ uint32_t x;
+ // big endian extract
+
+ x = byte_reader->data_ptr[0];
+ x <<= 8;
+ x |= byte_reader->data_ptr[1];
+ x <<= 8;
+ x |= byte_reader->data_ptr[2];
+ x <<= 8;
+ x |= byte_reader->data_ptr[3];
+ return x;
+
+}
+
+uint32_t bytereader_read_u32_be(bytereader_t byte_reader)
+{
+ uint32_t ret = bytereader_show_u32_be(byte_reader);
+ bytereader_advance(byte_reader, 4);
+ return ret;
+}
+
+/* 4 byte little-endian functions */
+uint32_t bytereader_show_u32_le(bytereader_t byte_reader)
+{
+ uint32_t x;
+ // little endian extract
+
+ x = byte_reader->data_ptr[3];
+ x <<= 8;
+ x |= byte_reader->data_ptr[2];
+ x <<= 8;
+ x |= byte_reader->data_ptr[1];
+ x <<= 8;
+ x |= byte_reader->data_ptr[0];
+ return x;
+}
+
+uint32_t bytereader_read_u32_le(bytereader_t byte_reader)
+{
+ uint32_t ret = bytereader_show_u32_le(byte_reader);
+ bytereader_advance(byte_reader, 4);
+ return ret;
+}
+
+/* float functions */
+float bytereader_show_f32_be(bytereader_t byte_reader)
+{
+ uint32_t ret = bytereader_show_u32_be(byte_reader);
+ return *(float *)(&ret);
+}
+
+float bytereader_read_f32_be(bytereader_t byte_reader)
+{
+ uint32_t ret = bytereader_read_u32_be(byte_reader);
+ return *(float *)(&ret);
+}
+
+GUID bytereader_read_uuid_be(bytereader_t byte_reader)
+{
+ GUID guid_value;
+ guid_value.Data1 = bytereader_read_u32_be(byte_reader);
+ guid_value.Data2 = bytereader_read_u16_be(byte_reader);
+ guid_value.Data3 = bytereader_read_u16_be(byte_reader);
+ bytereader_read_n(byte_reader, guid_value.Data4, 8);
+ return guid_value;
+}
+
+GUID bytereader_read_uuid_le(bytereader_t byte_reader)
+{
+ GUID guid_value;
+ guid_value.Data1 = bytereader_read_u32_le(byte_reader);
+ guid_value.Data2 = bytereader_read_u16_le(byte_reader);
+ guid_value.Data3 = bytereader_read_u16_le(byte_reader);
+ bytereader_read_n(byte_reader, guid_value.Data4, 8);
+ return guid_value;
+}
diff --git a/Src/replicant/nu/ByteReader.h b/Src/replicant/nu/ByteReader.h
new file mode 100644
index 000000000..31aa5d124
--- /dev/null
+++ b/Src/replicant/nu/ByteReader.h
@@ -0,0 +1,83 @@
+#pragma once
+#include <stdint.h>
+#include "foundation/types.h"
+
+/* A simple byte-oriented reader.
+use this instead of manual parsing as this deals with memory alignment issues
+for you.
+memory alignment can be critical and annoying on some architectures (e.g. PowerPC)
+it also handles little-endian/big-endian issues
+
+Usually you just make one of these things on the stack, passing in your buffer and length
+
+S is signed and U is unsigned
+Show functions will give you data w/o moving the stream position
+Align versions of the functions will assume the stream is properly aligned
+LE versions of the functions treat the byte stream as little-endian oriented
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct bytereader_struct_t
+ {
+ size_t byte_length;
+ const uint8_t *data_ptr;
+ const void *data;
+ } bytereader_value_t, bytereader_s, *bytereader_t;
+
+ void bytereader_init(bytereader_t byte_reader, const void *data, size_t byte_length);
+ static size_t bytereader_size(bytereader_t byte_reader) /* returns remaining bytes in stream */
+ {
+ return byte_reader->byte_length;
+ }
+ void bytereader_advance(bytereader_t byte_reader, size_t bytes); /* advances the byte stream */
+ void bytereader_reset(bytereader_t byte_reader); /* reset the data pointer and size back to the original position */
+ static const void *bytereader_pointer(bytereader_t byte_reader) /* returns a pointer to the current bitstream position */
+ {
+ return byte_reader->data_ptr;
+ }
+
+ /* returns the number of bytes to the next 0, or the end of the buffer */
+ size_t bytereader_find_zero(bytereader_t byte_reader);
+
+ /* n byte functions (basically memcpy) */
+ void bytereader_show_n(bytereader_t byte_reader, void *destination, size_t bytes);
+ void bytereader_read_n(bytereader_t byte_reader, void *destination, size_t bytes);
+
+ /* 1 byte functions */
+ uint8_t bytereader_show_u8(bytereader_t byte_reader);
+ uint8_t bytereader_read_u8(bytereader_t byte_reader);
+ int8_t bytereader_show_s8(bytereader_t byte_reader);
+ int8_t bytereader_read_s8(bytereader_t byte_reader);
+
+ /* 2 byte little endian functions */
+ uint16_t bytereader_show_u16_le(bytereader_t byte_reader);
+ uint16_t bytereader_read_u16_le(bytereader_t byte_reader);
+ int16_t bytereader_show_s16_le(bytereader_t byte_reader);
+ int16_t bytereader_read_s16_le(bytereader_t byte_reader);
+
+ /* 2 byte big-endian functions */
+ uint16_t bytereader_show_u16_be(bytereader_t byte_reader);
+ uint16_t bytereader_read_u16_be(bytereader_t byte_reader);
+ int16_t bytereader_show_s16_be(bytereader_t byte_reader);
+ int16_t bytereader_read_s16_be(bytereader_t byte_reader);
+
+ /* 4 byte big-endian functions */
+ uint32_t bytereader_show_u32_be(bytereader_t byte_reader);
+ uint32_t bytereader_read_u32_be(bytereader_t byte_reader);
+
+ /* 4 byte little-endian functions */
+ uint32_t bytereader_show_u32_le(bytereader_t byte_reader);
+ uint32_t bytereader_read_u32_le(bytereader_t byte_reader);
+
+ /* float functions */
+ float bytereader_show_f32_be(bytereader_t byte_reader);
+ float bytereader_read_f32_be(bytereader_t byte_reader);
+
+ GUID bytereader_read_uuid_be(bytereader_t byte_reader);
+ GUID bytereader_read_uuid_le(bytereader_t byte_reader);
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/ByteWriter.c b/Src/replicant/nu/ByteWriter.c
new file mode 100644
index 000000000..ab375a3f0
--- /dev/null
+++ b/Src/replicant/nu/ByteWriter.c
@@ -0,0 +1,77 @@
+#include "ByteWriter.h"
+#include <string.h>
+
+/* generic endian-agnostic implementation. this code assumes that unaligned accesses are illegal */
+void bytewriter_init(bytewriter_t byte_writer, void *data, size_t byte_length)
+{
+ byte_writer->data = data;
+ byte_writer->byte_length = byte_length;
+ byte_writer->data_ptr = (uint8_t *)data;
+}
+
+/* n byte functions */
+void bytewriter_write_n(bytewriter_t byte_writer, const void *source, size_t bytes)
+{
+ memcpy(byte_writer->data_ptr, source, bytes);
+ byte_writer->data_ptr += bytes;
+}
+
+void bytewriter_write_u8(bytewriter_t byte_writer, uint8_t value)
+{
+ *byte_writer->data_ptr++ = value;
+ byte_writer->byte_length--;
+}
+
+void bytewriter_write_u16_le(bytewriter_t byte_writer, uint16_t value)
+{
+ *byte_writer->data_ptr++ = value & 0xFF;
+ value >>= 8;
+ *byte_writer->data_ptr++ = value & 0xFF;
+ byte_writer->byte_length -= 2;
+}
+
+void bytewriter_write_u16_be(bytewriter_t byte_writer, uint16_t value)
+{
+ *byte_writer->data_ptr++ = (value >> 8) & 0xFF;
+ *byte_writer->data_ptr++ = value & 0xFF;
+ byte_writer->byte_length -= 2;
+}
+
+void bytewriter_write_u32_le(bytewriter_t byte_writer, uint32_t value)
+{
+ *byte_writer->data_ptr++ = value & 0xFF;
+ value >>= 8;
+ *byte_writer->data_ptr++ = value & 0xFF;
+ value >>= 8;
+ *byte_writer->data_ptr++ = value & 0xFF;
+ value >>= 8;
+ *byte_writer->data_ptr++ = value & 0xFF;
+ byte_writer->byte_length -= 4;
+}
+
+void bytewriter_write_u32_be(bytewriter_t byte_writer, uint32_t value)
+{
+ *byte_writer->data_ptr++ = (value >> 24) & 0xFF;
+ *byte_writer->data_ptr++ = (value >> 16) & 0xFF;
+ *byte_writer->data_ptr++ = (value >> 8) & 0xFF;
+ *byte_writer->data_ptr++ = value & 0xFF;
+ byte_writer->byte_length -= 4;
+}
+
+void bytewriter_write_zero_n(bytewriter_t byte_writer, size_t bytes)
+{
+ size_t i;
+ for (i=0;i<bytes;i++)
+ {
+ *byte_writer->data_ptr++ = 0;
+ }
+ byte_writer->byte_length -= bytes;
+}
+
+void bytewriter_write_uuid_be(bytewriter_t byte_writer, GUID guid_value)
+{
+ bytewriter_write_u32_be(byte_writer, guid_value.Data1);
+ bytewriter_write_u16_be(byte_writer, guid_value.Data2);
+ bytewriter_write_u16_be(byte_writer, guid_value.Data3);
+ bytewriter_write_n(byte_writer, guid_value.Data4, 8);
+}
diff --git a/Src/replicant/nu/ByteWriter.h b/Src/replicant/nu/ByteWriter.h
new file mode 100644
index 000000000..309bf106d
--- /dev/null
+++ b/Src/replicant/nu/ByteWriter.h
@@ -0,0 +1,48 @@
+#pragma once
+#include "foundation/types.h"
+/* A simple byte-oriented writer.
+use this instead of manually writing data as this deals with memory alignment issues
+for you.
+memory alignment can be critical and annoying on some architectures (e.g. PowerPC)
+it also handles little-endian/big-endian issues
+
+Usually you just make one of these things on the stack, passing in your buffer and length
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct bytewriter_struct_t
+ {
+ size_t byte_length;
+ uint8_t *data_ptr;
+ void *data;
+ } bytewriter_s, *bytewriter_t;
+
+ void bytewriter_init(bytewriter_t byte_writer, void *data, size_t byte_length);
+ static size_t bytewriter_size(bytewriter_t byte_writer) /* returns remaining bytes in stream */
+ {
+ return byte_writer->byte_length;
+ }
+ static void *bytewriter_pointer(bytewriter_t byte_writer) /* returns a pointer to the current bitstream position */
+ {
+ return byte_writer->data_ptr;
+ }
+ static void bytewriter_advance(bytewriter_t byte_writer, size_t bytes) /* advances the byte stream */
+ {
+ byte_writer->byte_length-=bytes;
+ byte_writer->data_ptr+=bytes;
+ }
+
+ void bytewriter_write_n(bytewriter_t byte_writer, const void *source, size_t bytes);
+ void bytewriter_write_zero_n(bytewriter_t byte_writer, size_t bytes);
+ void bytewriter_write_u8(bytewriter_t byte_writer, uint8_t value);
+ void bytewriter_write_u16_le(bytewriter_t byte_writer, uint16_t value);
+ void bytewriter_write_u16_be(bytewriter_t byte_writer, uint16_t value);
+ void bytewriter_write_u32_le(bytewriter_t byte_writer, uint32_t value);
+ void bytewriter_write_u32_be(bytewriter_t byte_writer, uint32_t value);
+ void bytewriter_write_uuid_be(bytewriter_t byte_writer, GUID guid_value);
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/LockFreeFIFO.cpp b/Src/replicant/nu/LockFreeFIFO.cpp
new file mode 100644
index 000000000..3731d7cd6
--- /dev/null
+++ b/Src/replicant/nu/LockFreeFIFO.cpp
@@ -0,0 +1,110 @@
+#include "LockFreeFIFO.h"
+#include "foundation/align.h"
+#include "foundation/atomics.h"
+
+#if 1 //def USE_VISTA_UP_API
+static int CAS2(FIFO_POINTER *fifo, queue_node_t *tail, size_t icount, queue_node_t *next, size_t icount2)
+{
+ NALIGN(8) FIFO_POINTER compare = { tail, icount };
+ NALIGN(8) FIFO_POINTER exchange = { next, icount2 };
+ return nx_atomic_cmpxchg2(*(int64_t *)&compare, *(int64_t *)&exchange, (volatile int64_t *)fifo);
+ //return atomic_compare_exchange_doublepointer((volatile intptr2_t *)fifo, *(intptr2_t *)&exchange, *(intptr2_t *)&compare);
+}
+#else
+inline char CAS2 (volatile void * addr, volatile void * v1, volatile long v2, void * n1, long n2)
+{
+ register char c;
+ __asm {
+ push ebx
+ push ecx
+ push edx
+ push esi
+ mov esi, addr
+ mov eax, v1
+ mov ebx, n1
+ mov ecx, n2
+ mov edx, v2
+ LOCK cmpxchg8b qword ptr [esi]
+ sete c
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ }
+ return c;
+}
+#endif
+
+static int CAS(queue_node_t **fifo, queue_node_t *compare, queue_node_t *exchange)
+{
+ return nx_atomic_cmpxchg_pointer(compare, exchange, (void* volatile *)fifo);
+}
+
+#define ENDFIFO(ff) ((queue_node_t*)0)
+void fifo_init(fifo_t *fifo)
+{
+ fifo->dummy.Next = ENDFIFO(fifo); //makes the cell the only cell in the list
+ fifo->head.fifo_node_t = fifo->tail.fifo_node_t = &fifo->dummy;
+ fifo->head.count = fifo->tail.count = 0;
+ fifo->count = 0;
+}
+
+void fifo_push(fifo_t *fifo, queue_node_t *cl)
+{
+ queue_node_t * volatile tail;
+ size_t icount;
+ cl->Next = ENDFIFO(fifo); // // set the cell next pointer to end marker
+ for (;;) // try until enqueue is done
+ {
+ icount = fifo->tail.count; // read the _tail modification count
+ tail = fifo->tail.fifo_node_t; // read the _tail cell
+ if (CAS(&tail->Next, ENDFIFO(fifo), cl)) // try to link the cell to the _tail cell
+ {
+ break; // enqueue is done, exit the loop
+ }
+ else // _tail was not pointing to the last cell, try to set _tail to the next cell
+ {
+ CAS2(&fifo->tail, tail, icount, tail->Next, icount+1);
+ }
+ }
+ CAS2 (&fifo->tail, tail, icount, cl, icount+1); // enqueue is done, try to set _tail to the enqueued cell
+ nx_atomic_inc(&fifo->count);
+}
+
+queue_node_t *fifo_pop(fifo_t *fifo)
+{
+ queue_node_t * volatile head;
+ for (;;)
+ {
+ size_t ocount = fifo->head.count; // read the _head modification count
+ size_t icount = fifo->tail.count; // read the _tail modification count
+ head = fifo->head.fifo_node_t; // read the _head cell
+ queue_node_t *next = head->Next; // read the next cell
+ if (ocount == fifo->head.count) // ensures that next is a valid pointer to avoid failure when reading next value
+ {
+ if (head == fifo->tail.fifo_node_t) // is queue empty or _tail falling behind ?
+ {
+ if (next == ENDFIFO(fifo)) // is queue empty ?
+ {
+ return 0; // queue is empty: return NULL
+ }
+ // _tail is pointing to _head in a non empty queue, try to set _tail to the next cell
+ CAS2(&fifo->tail, head, icount, next, icount+1);
+ }
+ else if (next != ENDFIFO(fifo)) // if we are not competing on the dummy next
+ {
+ if (CAS2(&fifo->head, head, ocount, next, ocount+1)) // try to set _head to the next cell
+ {
+ break;
+ }
+ }
+ }
+ }
+ nx_atomic_dec(&fifo->count);
+ if (head == &fifo->dummy)
+ {
+ fifo_push(fifo,head);
+ head = fifo_pop(fifo);
+ }
+ return head;
+}
diff --git a/Src/replicant/nu/LockFreeFIFO.h b/Src/replicant/nu/LockFreeFIFO.h
new file mode 100644
index 000000000..598cfaeb2
--- /dev/null
+++ b/Src/replicant/nu/LockFreeFIFO.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "queue_node.h"
+/* Algorithm taken from
+Lock-Free Techniques for Concurrent Access to Shared Objects
+Dominique Fober, Yann Orlarey, Stephane Letz
+http://www.grame.fr/Ressources/pub/LockFree.pdf
+http://nedko.arnaudov.name/soft/L17_Fober.pdf
+http://www.grame.fr/Ressources/pub/TR-050523.pdf
+This implementation (c) 2010 Nullsoft, Inc.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct FIFO_POINTER
+{
+ queue_node_t *fifo_node_t;
+ size_t count;
+};
+
+struct fifo_t
+{
+ FIFO_POINTER head; // _head pointer and total count of pop operations (ocount)
+ FIFO_POINTER tail; // _tail pointer and total count of push operations (icount)
+ queue_node_t dummy;
+ size_t count;
+};
+void fifo_init(fifo_t *fifo);
+void fifo_push(fifo_t *fifo, queue_node_t *cl);
+queue_node_t *fifo_pop(fifo_t *fifo);
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nu/LockFreeItem.h b/Src/replicant/nu/LockFreeItem.h
new file mode 100644
index 000000000..09ccafe39
--- /dev/null
+++ b/Src/replicant/nu/LockFreeItem.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "foundation/atomics.h"
+#include "lfitem.h"
+/* This data structure holds one item, and returns you the old item when you replace it
+ it's sort of a "stack of 1" */
+
+template <class value_t>
+class LockFreeItem
+{
+public:
+ typedef value_t *ptr_t;
+ LockFreeItem()
+ {
+ lfitem_init(&item);
+ }
+
+ value_t *GetItem()
+ {
+ return (value_t *)lfitem_get(&item);
+ }
+
+ // returns the previous value
+ value_t *SetItem(value_t *new_item)
+ {
+ return (value_t *)lfitem_set(&item, new_item);
+ }
+
+ // if there's already a value, returns what you passed in
+ value_t *SetItemIfZero(value_t *new_item)
+ {
+ return (value_t *)lfitem_set_if_zero(&item, new_item);
+ }
+
+ lfitem_value_t item;
+};
diff --git a/Src/replicant/nu/LockFreeLIFO.c b/Src/replicant/nu/LockFreeLIFO.c
new file mode 100644
index 000000000..eb9c1eb1f
--- /dev/null
+++ b/Src/replicant/nu/LockFreeLIFO.c
@@ -0,0 +1,61 @@
+#include "LockFreeLIFO.h"
+#include "foundation/atomics.h"
+
+/* TODO: on windows, replace with InitializeSListHead/InterlockedPushEntrySList/InterlockedPopEntrySList just to be safe */
+void lifo_init(lifo_t *lifo)
+{
+ lifo->head = 0;
+}
+
+void lifo_push(lifo_t *lifo, queue_node_t *cl)
+{
+ queue_node_t *new_head = cl;
+ queue_node_t *old_head = 0;
+ do
+ {
+ old_head = (queue_node_t *)lifo->head;
+ new_head->Next = old_head;
+ } while (!nx_atomic_cmpxchg_pointer(old_head, new_head, (void * volatile *)&lifo->head));
+}
+
+queue_node_t *lifo_pop(lifo_t *lifo)
+{
+ queue_node_t *new_head = 0, *old_head = 0;
+ do
+ {
+ old_head = (queue_node_t *)lifo->head;
+ if (old_head)
+ new_head = old_head->Next;
+ else
+ new_head = 0;
+ } while (!nx_atomic_cmpxchg_pointer(old_head, new_head, (void * volatile *)&lifo->head));
+ return old_head;
+}
+
+ queue_node_t *lifo_malloc(size_t bytes)
+ {
+#ifdef __GNUC__
+# ifdef __APPLE__
+ void *v = 0;
+ (void) posix_memalign(&v, sizeof(void *), bytes);
+ return v;
+# else
+ return memalign(bytes, sizeof(void *));
+# endif
+#elif defined(_WIN32)
+ return _aligned_malloc(bytes, MEMORY_ALLOCATION_ALIGNMENT);
+#else
+#error port me!
+#endif
+ }
+
+ void lifo_free(queue_node_t *ptr)
+ {
+#ifdef __GNUC__
+ free(ptr);
+#elif defined(_WIN32)
+ _aligned_free(ptr);
+#else
+#error port me!
+#endif
+ }
diff --git a/Src/replicant/nu/LockFreeLIFO.h b/Src/replicant/nu/LockFreeLIFO.h
new file mode 100644
index 000000000..b96f0c43f
--- /dev/null
+++ b/Src/replicant/nu/LockFreeLIFO.h
@@ -0,0 +1,18 @@
+#pragma once
+#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
+#include "android-armv7/LockFreeLIFO.h"
+#elif defined(__ANDROID__)
+#include "android-arm/LockFreeLIFO.h"
+#elif defined(_WIN32) && defined(_M_IX86)
+#include "win-x86/LockFreeLIFO.h"
+#elif defined(_WIN32) && defined(_M_X64)
+#include "win-amd64/LockFreeLIFO.h"
+#elif defined(__APPLE__) && defined(__amd64__)
+#include "osx-amd64/LockFreeLIFO.h"
+#elif defined(__APPLE__) && defined(__i386__)
+#include "osx-x86/LockFreeLIFO.h"
+#elif defined(__linux__)
+#include "linux/LockFreeLIFO.h"
+#else
+#error port me!
+#endif
diff --git a/Src/replicant/nu/LockFreeRingBuffer.cpp b/Src/replicant/nu/LockFreeRingBuffer.cpp
new file mode 100644
index 000000000..75edb56c9
--- /dev/null
+++ b/Src/replicant/nu/LockFreeRingBuffer.cpp
@@ -0,0 +1,421 @@
+/*
+ * LockFreeRingBuffer.cpp
+ * Lock-free ring buffer data structure.
+ * One thread can be consumer and one can be producer
+ *
+ * Created by Ben Allison on 11/10/07.
+ * Copyright 2007 Nullsoft, Inc. All rights reserved.
+ *
+ */
+
+#include "LockFreeRingBuffer.h"
+#include "foundation/types.h"
+#include "foundation/atomics.h"
+#include "foundation/error.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#define MIN(a,b) ((a<b)?(a):(b))
+
+
+LockFreeRingBuffer::LockFreeRingBuffer()
+{
+ ringBuffer=0;
+ ringBufferSize=0;
+ ringBufferUsed=0;
+ ringWritePosition=0;
+ ringReadPosition=0;
+}
+
+LockFreeRingBuffer::~LockFreeRingBuffer()
+{
+ free(ringBuffer);
+ ringBuffer=0;
+}
+
+void LockFreeRingBuffer::Reset()
+{
+ free(ringBuffer);
+ ringBuffer=0;
+}
+
+bool LockFreeRingBuffer::reserve(size_t bytes)
+{
+ void *new_ring_buffer = realloc(ringBuffer, bytes);
+ if (!new_ring_buffer)
+ return false;
+
+ ringBufferSize=bytes;
+ ringBuffer = (char *)new_ring_buffer;
+ clear();
+ return true;
+}
+
+int LockFreeRingBuffer::expand(size_t bytes)
+{
+
+ if (bytes > ringBufferSize)
+ {
+ char *new_buffer = (char *)realloc(ringBuffer, bytes);
+ if (!new_buffer)
+ return NErr_OutOfMemory;
+
+ size_t write_offset = ringReadPosition-ringBuffer;
+ size_t read_offset = ringWritePosition-ringBuffer;
+
+ /* update write pointer for the new buffer */
+ ringWritePosition = new_buffer + write_offset;
+
+ if (write_offset > read_offset || !ringBufferUsed) /* ringBufferUsed will resolve the ambiguity when ringWritePosition == ringReadPosition */
+ {
+ /* the ring buffer looks like [ RXXXW ], so we don't need to move anything.
+ Just update the read pointer */
+
+ ringReadPosition = new_buffer + write_offset;
+ }
+ else
+ {
+ /* [XXW RXX] needs to become [XXW RXX] */
+ size_t end_bytes = ringBufferSize-write_offset; // number of bytes that we need to relocate (the RXX portion)
+ char *new_read_pointer = &new_buffer[bytes - end_bytes];
+ memmove(new_read_pointer, ringReadPosition, end_bytes);
+ ringReadPosition = new_read_pointer; /* update read pointer */
+ }
+ ringBufferSize=bytes;
+ ringBuffer = new_buffer;
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ return NErr_Success;
+ }
+ else
+ return NErr_NoAction;
+}
+
+
+bool LockFreeRingBuffer::empty() const
+{
+ return (ringBufferUsed==0);
+}
+
+size_t LockFreeRingBuffer::read(void *dest, size_t len)
+{
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+ size_t toCopy=ringBufferUsed;
+ if (toCopy > len) toCopy = len;
+
+ size_t copied=0;
+ len-=toCopy;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ size_t read1 = MIN(end, toCopy);
+ memcpy(out, ringReadPosition, read1);
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ copied+=read1;
+ ringReadPosition+=read1;
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+
+ // update positions
+ nx_atomic_sub(read1, &ringBufferUsed);
+ toCopy-=read1;
+ out = (int8_t *)out+read1;
+
+ // see if we still have more to read after wrapping around
+ if (toCopy)
+ {
+ memcpy(out, ringReadPosition, toCopy);
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ copied+=toCopy;
+ ringReadPosition+=toCopy;
+ nx_atomic_sub(toCopy, &ringBufferUsed);
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t LockFreeRingBuffer::advance_to(size_t position)
+{
+ intptr_t bytes_to_flush = (intptr_t)(position - (size_t)ringReadPosition);
+ if (bytes_to_flush < 0)
+ bytes_to_flush += ringBufferSize;
+ return advance(bytes_to_flush);
+}
+
+size_t LockFreeRingBuffer::at(size_t offset, void *dest, size_t len) const
+{
+ size_t toCopy=ringBufferUsed;
+
+ // make a local copy of this so we don't blow the original
+ char *ringReadPosition = this->ringReadPosition;
+
+ /* --- do a "dummy read" to deal with the offset request --- */
+ size_t dummy_end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ offset = MIN(toCopy, offset);
+ size_t read0 = MIN(dummy_end, offset);
+ ringReadPosition+=read0;
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+
+ // update positions
+ toCopy-=read0;
+ offset-=read0;
+
+ // do second-half read (wraparound)
+ if (offset)
+ {
+ ringReadPosition+=offset;
+ toCopy-=offset;
+ }
+
+ // dummy read done
+
+ /* --- set up destination buffer and copy size --- */
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+
+ if (toCopy > len) toCopy=len;
+ size_t copied=0;
+
+ /* --- read to the end of the ring buffer --- */
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ size_t read1 = MIN(end, toCopy);
+ memcpy(out, ringReadPosition, read1);
+ copied+=read1;
+ ringReadPosition+=read1;
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+
+ // update positions
+ toCopy-=read1;
+ out = (int8_t *)out+read1;
+
+ /* --- see if we still have more to read after wrapping around --- */
+ if (toCopy)
+ {
+ memcpy(out, ringReadPosition, toCopy);
+ copied+=toCopy;
+ ringReadPosition+=toCopy;
+ }
+
+ return copied;
+}
+
+size_t LockFreeRingBuffer::peek(void *dest, size_t len) const
+{
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+
+ size_t toCopy=ringBufferUsed;
+
+ if (toCopy > len) toCopy=len;
+ size_t copied=0;
+
+ // make a local copy of this so we don't blow the original
+ char *ringReadPosition = this->ringReadPosition;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ size_t read1 = MIN(end, toCopy);
+ memcpy(out, ringReadPosition, read1);
+ copied+=read1;
+ ringReadPosition+=read1;
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+
+ // update positions
+ toCopy-=read1;
+ out = (int8_t *)out+read1;
+
+ // see if we still have more to read after wrapping around
+ if (toCopy)
+ {
+ memcpy(out, ringReadPosition, toCopy);
+ copied+=toCopy;
+ ringReadPosition+=toCopy;
+ }
+
+ return copied;
+}
+
+size_t LockFreeRingBuffer::advance(size_t len)
+{
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ size_t toCopy=ringBufferUsed;
+
+ if (toCopy>len) toCopy=len;
+ size_t copied=0;
+ len-=toCopy;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ size_t read1 = MIN(end, toCopy);
+ copied+=read1;
+ ringReadPosition+=read1;
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+
+ // update positions
+ toCopy-=read1;
+ nx_atomic_sub(read1, &ringBufferUsed);
+
+ // see if we still have more to read after wrapping around
+ if (toCopy)
+ {
+ copied+=toCopy;
+ ringReadPosition+=toCopy;
+ nx_atomic_sub(toCopy, &ringBufferUsed);
+
+ if (ringReadPosition == ringBuffer + ringBufferSize)
+ ringReadPosition=ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t LockFreeRingBuffer::avail() const
+{
+ return ringBufferSize - ringBufferUsed;
+}
+
+size_t LockFreeRingBuffer::write(const void *buffer, size_t bytes)
+{
+ size_t used=ringBufferUsed;
+
+ size_t avail = ringBufferSize - used;
+ bytes = MIN(avail, bytes);
+
+ // write to the end of the ring buffer
+ size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
+ size_t copied=0;
+ size_t write1 = MIN(end, bytes);
+ memcpy(ringWritePosition, buffer, write1);
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ copied+=write1;
+ ringWritePosition+=write1;
+ if (ringWritePosition == ringBuffer + ringBufferSize)
+ ringWritePosition=ringBuffer;
+
+
+ // update positions
+ nx_atomic_add(write1, &ringBufferUsed);
+ bytes-=write1;
+ buffer = (const int8_t *)buffer+write1;
+
+ // see if we still have more to write after wrapping around
+ if (bytes)
+ {
+ memcpy(ringWritePosition, buffer, bytes);
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ copied+=bytes;
+ ringWritePosition+=bytes;
+ nx_atomic_add(bytes, &ringBufferUsed);
+ if (ringWritePosition == ringBuffer + ringBufferSize)
+ ringWritePosition=ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t LockFreeRingBuffer::update(size_t bytes)
+{
+ size_t used=ringBufferUsed;
+
+ size_t avail = ringBufferSize - used;
+ bytes = MIN(avail, bytes);
+
+ // write to the end of the ring buffer
+ size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
+ size_t copied=0;
+ size_t write1 = MIN(end, bytes);
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ copied+=write1;
+ ringWritePosition+=write1;
+ if (ringWritePosition == ringBuffer + ringBufferSize)
+ ringWritePosition=ringBuffer;
+
+ // update positions
+ nx_atomic_add(write1, &ringBufferUsed);
+ bytes-=write1;
+
+ // see if we still have more to write after wrapping around
+ if (bytes)
+ {
+ /* no need for memory barrier here, we havn't written anything in the interim */
+ copied+=bytes;
+ ringWritePosition+=bytes;
+ nx_atomic_add(bytes, &ringBufferUsed);
+ if (ringWritePosition == ringBuffer + ringBufferSize)
+ ringWritePosition=ringBuffer;
+ }
+
+ return copied;
+}
+
+
+void LockFreeRingBuffer::get_write_buffer(size_t bytes, void **buffer, size_t *bytes_available)
+{
+ size_t used=ringBufferUsed;
+
+ size_t avail = ringBufferSize - used;
+ bytes = MIN(avail, bytes);
+
+ // can only write to the end of the ring buffer
+ size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
+ *bytes_available = MIN(end, bytes);
+ *buffer = ringWritePosition;
+}
+
+void LockFreeRingBuffer::get_read_buffer(size_t bytes, const void **buffer, size_t *bytes_available)
+{
+ size_t toCopy=ringBufferUsed;
+
+ if (toCopy > bytes) toCopy=bytes;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
+
+ *bytes_available = MIN(end, toCopy);
+ *buffer = ringReadPosition;
+}
+
+size_t LockFreeRingBuffer::size() const
+{
+ return ringBufferUsed;
+}
+
+void LockFreeRingBuffer::clear()
+{
+ nx_atomic_write(0, &ringBufferUsed);
+ ringWritePosition=ringBuffer;
+ ringReadPosition=ringBuffer;
+}
+
+size_t LockFreeRingBuffer::write_position() const
+{
+ return (size_t)ringWritePosition;
+}
+
+size_t LockFreeRingBuffer::read_position() const
+{
+ return (size_t)ringReadPosition;
+}
diff --git a/Src/replicant/nu/LockFreeRingBuffer.h b/Src/replicant/nu/LockFreeRingBuffer.h
new file mode 100644
index 000000000..365146937
--- /dev/null
+++ b/Src/replicant/nu/LockFreeRingBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * Created by Ben Allison on 11/10/07.
+ * Copyright 2007-2011 Nullsoft, Inc. All rights reserved.
+ *
+ * Ring Buffer class
+ * Thread safety:
+ * This class can be used from exactly two simultaneous threads without locking
+ * as long as one thread only writes and the other thread only reads
+ * the writer thread may call empty(), avail(), size(), write(), fill(
+ * the reader thread my call empty(), avail(), size(), read(), peek(), advance()
+ *
+ * two (or more) readers or two (or more) writers requires external locking
+ *
+ * Reset(), reserve(), clear(), LockBuffer(), UnlockBuffer() are not thread-safe
+ */
+
+#pragma once
+#include <stddef.h>
+#include "foundation/types.h"
+
+class LockFreeRingBuffer
+{
+public:
+ LockFreeRingBuffer();
+ ~LockFreeRingBuffer();
+ void Reset();
+ int expand(size_t bytes); // like reserve, but only expands upward. non-destructive. returns an NError
+ bool reserve(size_t bytes);
+ bool empty() const;
+ size_t avail() const; // how much available for writing
+ size_t size() const; // how much available for reading
+ void clear();
+ size_t read(void *dest, size_t len); // returns amount actually read
+ size_t advance(size_t len); // same as read() but doesn't write the data any where.
+ size_t peek(void *dest, size_t len) const; // same as read() but doesn't advance the read pointer
+ size_t write(const void *src, size_t len);
+ size_t update(size_t len); // same as write() but doesn't write the data, usually used after a get_buffer call
+ size_t at(size_t offset, void *dest, size_t len) const; // peeks() from offset. returns bytes read
+ size_t write_position() const; // returns an integer representing a write position
+ size_t read_position() const; // returns an integer representing the read position
+ size_t advance_to(size_t position); // moves the read pointer to a position previously returned from write_position(). returns bytes advanced
+ void get_write_buffer(size_t bytes, void **buffer, size_t *bytes_available); /* returns a pointer that you can write data to, you MUST call update() when you are done */
+ void get_read_buffer(size_t bytes, const void **buffer, size_t *bytes_available); /* returns a pointer that you can read data from, call advance() when you are done */
+
+
+private:
+ volatile size_t ringBufferUsed;
+ size_t ringBufferSize;
+ char *ringBuffer;
+ char *ringWritePosition;
+ char *ringReadPosition;
+}; \ No newline at end of file
diff --git a/Src/replicant/nu/MessageLoop.h b/Src/replicant/nu/MessageLoop.h
new file mode 100644
index 000000000..5b7548d6e
--- /dev/null
+++ b/Src/replicant/nu/MessageLoop.h
@@ -0,0 +1,16 @@
+#pragma once
+#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
+#include "android-armv7/MessageLoop.h"
+#elif defined(__ANDROID__) && (defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__))
+#include "android-arm/MessageLoop.h"
+#elif defined(__ANDROID__) && defined(__i386__)
+#include "android-x86/MessageLoop.h"
+#elif defined(_WIN32)
+#include "win/MessageLoop.h"
+#elif defined(__linux__)
+#include "linux/MessageLoop.h"
+#elif defined(__APPLE__)
+#include "osx/MessageLoop.h"
+#else
+#error port me!
+#endif
diff --git a/Src/replicant/nu/Pairs.h b/Src/replicant/nu/Pairs.h
new file mode 100644
index 000000000..80438ba67
--- /dev/null
+++ b/Src/replicant/nu/Pairs.h
@@ -0,0 +1,15 @@
+#pragma once
+namespace nu
+{
+
+ template <class A, class B>
+ class Pair
+ {
+ public:
+ Pair() {}
+ Pair(const A &_a, const B &_b) : first(_a), second(_b) {}
+
+ A first;
+ B second;
+ };
+}
diff --git a/Src/replicant/nu/ProgressTracker.cpp b/Src/replicant/nu/ProgressTracker.cpp
new file mode 100644
index 000000000..e42cada30
--- /dev/null
+++ b/Src/replicant/nu/ProgressTracker.cpp
@@ -0,0 +1,117 @@
+#include "ProgressTracker.h"
+#include <stdio.h>
+/* Helper class for managing valid chunks in non-sequential scenarios
+e.g. progressive downloading */
+
+ProgressTracker::ProgressTracker()
+{
+ current_position=0;
+ current_chunk=0;
+ chunks[0]=0;
+ chunks[null_position]=null_position;
+};
+
+void ProgressTracker::Write(uint64_t bytes_written)
+{
+ nu::AutoLock list_lock(list_guard);
+ ChunkList::iterator next, itr = chunks.find(current_chunk);
+ current_position += bytes_written;
+ if (itr->second < current_position)
+ itr->second = current_position;
+
+ for (;;)
+ {
+ next = itr;
+ next++;
+ if (next != chunks.end() && (next->first <= itr->second))
+ {
+ itr->second = next->second;
+ chunks.erase(next);
+ }
+ else
+ break;
+ }
+}
+
+bool ProgressTracker::Valid(uint64_t requested_position, uint64_t requested_end, uint64_t *available)
+{
+ nu::AutoLock list_lock(list_guard);
+ for (ChunkList::iterator itr=chunks.begin();itr!=chunks.end();itr++)
+ {
+ if (requested_position >= itr->first)
+ {
+ if (requested_position < itr->second)
+ {
+ if (available)
+ *available = itr->second - requested_position;
+
+ if (requested_end <= itr->second)
+ return true;
+ else
+ return false;
+
+ }
+ }
+ }
+ if (available)
+ *available = 0;
+ return false;
+}
+
+bool ProgressTracker::Seek(uint64_t requested_position, uint64_t requested_end, uint64_t *new_start, uint64_t *new_end)
+{
+ nu::AutoLock list_lock(list_guard);
+ uint64_t last_good_start=0;
+ ChunkList::iterator itr;
+ for (itr=chunks.begin();itr!=chunks.end();itr++)
+ {
+ if (requested_position >= itr->first)
+ {
+ current_chunk = itr->first;
+ if (requested_position <= itr->second)
+ {
+ ChunkList::iterator next = itr;
+ next++;
+ *new_end = next->first;
+
+ *new_start = current_position = itr->second;
+
+ if (requested_end <= itr->second)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ last_good_start = itr->second;
+ }
+ }
+ }
+ if (last_good_start > requested_position)
+ *new_start = current_position = last_good_start;
+ else
+ {
+
+ *new_start = current_chunk = current_position = requested_position;
+ chunks[current_chunk] = current_chunk;
+ }
+ *new_end = null_position;
+ return false;
+}
+
+void ProgressTracker::Dump()
+{
+ ChunkList::iterator itr;
+ for (itr=chunks.begin();itr!=chunks.end();itr++)
+ {
+ printf("%llu - %llu\n", itr->first, itr->second);
+ }
+
+}
+
+
+const uint64_t ProgressTracker::null_position = (uint64_t)-1;
diff --git a/Src/replicant/nu/ProgressTracker.h b/Src/replicant/nu/ProgressTracker.h
new file mode 100644
index 000000000..83f018026
--- /dev/null
+++ b/Src/replicant/nu/ProgressTracker.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "foundation/types.h"
+#include <map>
+#include "nu/AutoLock.h"
+
+/* Helper class for managing valid chunks in non-sequential scenarios
+e.g. progressive downloading */
+
+class ProgressTracker
+{
+public:
+ static const uint64_t null_position;
+ ProgressTracker();
+ void Write(uint64_t bytes_written);
+ bool Valid(uint64_t requested_position, uint64_t requested_end, uint64_t *available=0);
+ bool Seek(uint64_t requested_position, uint64_t requested_end, uint64_t *new_start, uint64_t *new_end);
+ void Dump();
+
+ typedef std::map<uint64_t, uint64_t> ChunkList;
+ ChunkList chunks;
+ uint64_t current_chunk;
+ uint64_t current_position;
+ nu::LockGuard list_guard;
+};
diff --git a/Src/replicant/nu/RingBuffer.cpp b/Src/replicant/nu/RingBuffer.cpp
new file mode 100644
index 000000000..dfc47ef1e
--- /dev/null
+++ b/Src/replicant/nu/RingBuffer.cpp
@@ -0,0 +1,464 @@
+/*
+ * RingBuffer.cpp
+ * Lock-free ring buffer data structure.
+ * One thread can be consumer and one can be producer
+ *
+ * Created by Ben Allison on 11/10/07.
+ * Copyright 2007 Nullsoft, Inc. All rights reserved.
+ *
+ */
+
+#include "RingBuffer.h"
+#include "foundation/error.h"
+#include "foundation/types.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#define MIN(a,b) ((a<b)?(a):(b))
+
+
+RingBuffer::RingBuffer()
+{
+ ringBuffer = 0;
+ ringBufferSize = 0;
+ ringBufferUsed = 0;
+ ringWritePosition = 0;
+ ringReadPosition = 0;
+}
+
+RingBuffer::~RingBuffer()
+{
+ if ( ringBuffer )
+ free( ringBuffer );
+
+ ringBuffer = 0;
+}
+
+void RingBuffer::Reset()
+{
+ if ( ringBuffer )
+ free( ringBuffer );
+
+ ringBuffer = 0;
+}
+
+bool RingBuffer::reserve( size_t bytes )
+{
+ Reset();
+
+ ringBufferSize = bytes;
+
+ ringBuffer = (char *)calloc( ringBufferSize, sizeof( char ) );
+ if ( !ringBuffer )
+ return false;
+
+ clear();
+
+ return true;
+}
+
+int RingBuffer::expand( size_t bytes )
+{
+ if ( bytes > ringBufferSize )
+ {
+ char *new_buffer = (char *)realloc( ringBuffer, bytes );
+ if ( !new_buffer )
+ return NErr_OutOfMemory;
+
+ size_t write_offset = ringReadPosition - ringBuffer;
+ size_t read_offset = ringWritePosition - ringBuffer;
+
+ /* update write pointer for the new buffer */
+ ringWritePosition = new_buffer + write_offset;
+
+ if ( write_offset > read_offset || !ringBufferUsed ) /* ringBufferUsed will resolve the ambiguity when ringWritePosition == ringReadPosition */
+ {
+ /* the ring buffer looks like [ RXXXW ], so we don't need to move anything.
+ Just update the read pointer */
+
+ ringReadPosition = new_buffer + write_offset;
+ }
+ else
+ {
+ /* [XXW RXX] needs to become [XXW RXX] */
+ size_t end_bytes = ringBufferSize - read_offset; // number of bytes that we need to relocate (the RXX portion)
+ char *new_read_pointer = &new_buffer[ bytes - end_bytes ];
+
+ memmove( new_read_pointer, ringReadPosition, end_bytes );
+
+ ringReadPosition = new_read_pointer; /* update read pointer */
+ }
+
+ ringBufferSize = bytes;
+ ringBuffer = new_buffer;
+
+ return NErr_Success;
+ }
+ else
+ return NErr_NoAction;
+}
+
+bool RingBuffer::empty() const
+{
+ return ( ringBufferUsed == 0 );
+}
+
+size_t RingBuffer::read( void *dest, size_t len )
+{
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+ size_t toCopy = MIN( ringBufferUsed, len );
+ size_t copied = 0;
+
+ len -= toCopy;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ memcpy( out, ringReadPosition, read1 );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ ringBufferUsed -= read1;
+ toCopy -= read1;
+ out = (int8_t *)out + read1;
+
+ // see if we still have more to read after wrapping around
+ if ( toCopy )
+ {
+ memcpy( out, ringReadPosition, toCopy );
+
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ ringBufferUsed -= toCopy;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::at( size_t offset, void *dest, size_t len ) const
+{
+ size_t toCopy = ringBufferUsed;
+
+ // make a local copy of this so we don't blow the original
+ char *ringReadPosition = this->ringReadPosition;
+
+ /* --- do a "dummy read" to deal with the offset request --- */
+ size_t dummy_end = ringBufferSize - ( ringReadPosition - ringBuffer );
+
+ offset = MIN( toCopy, offset );
+ size_t read0 = MIN( dummy_end, offset );
+ ringReadPosition += read0;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read0;
+ offset -= read0;
+
+ // do second-half read (wraparound)
+ if ( offset )
+ {
+ ringReadPosition += offset;
+ toCopy -= offset;
+ }
+
+ // dummy read done
+
+ /* --- set up destination buffer and copy size --- */
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+
+ if ( toCopy > len )
+ toCopy = len;
+
+ size_t copied = 0;
+
+ /* --- read to the end of the ring buffer --- */
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ memcpy( out, ringReadPosition, read1 );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read1;
+ out = (int8_t *)out + read1;
+
+ /* --- see if we still have more to read after wrapping around --- */
+ if ( toCopy )
+ {
+ memcpy( out, ringReadPosition, toCopy );
+
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::peek( void *dest, size_t len ) const
+{
+ int8_t *out = (int8_t *)dest; // lets us do pointer math easier
+
+ size_t toCopy = MIN( ringBufferUsed, len );
+ size_t copied = 0;
+
+ // make a local copy of this so we don't blow the original
+ char *ringReadPosition = this->ringReadPosition;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ memcpy( out, ringReadPosition, read1 );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read1;
+ out = (int8_t *)out + read1;
+
+ // see if we still have more to read after wrapping around
+ if ( toCopy )
+ {
+ memcpy( out, ringReadPosition, toCopy );
+
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::advance( size_t len )
+{
+ size_t toCopy = MIN( ringBufferUsed, len );
+ size_t copied = 0;
+
+ len -= toCopy;
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t read1 = MIN( end, toCopy );
+
+ copied += read1;
+ ringReadPosition += read1;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ toCopy -= read1;
+ ringBufferUsed -= read1;
+
+ // see if we still have more to read after wrapping around
+ if ( toCopy )
+ {
+ copied += toCopy;
+ ringReadPosition += toCopy;
+ ringBufferUsed -= toCopy;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::avail() const
+{
+ return ringBufferSize - ringBufferUsed;
+}
+
+size_t RingBuffer::write( const void *buffer, size_t bytes )
+{
+ size_t used = ringBufferUsed;
+ size_t avail = ringBufferSize - used;
+
+ bytes = MIN( avail, bytes );
+
+ // write to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringWritePosition - ringBuffer );
+ size_t copied = 0;
+ size_t write1 = MIN( end, bytes );
+
+ memcpy( ringWritePosition, buffer, write1 );
+
+ copied += write1;
+ ringWritePosition += write1;
+
+ if ( ringWritePosition == ringBuffer + ringBufferSize )
+ ringWritePosition = ringBuffer;
+
+ // update positions
+ ringBufferUsed += write1;
+ bytes -= write1;
+ buffer = (const int8_t *)buffer + write1;
+
+ // see if we still have more to write after wrapping around
+ if ( bytes )
+ {
+ memcpy( ringWritePosition, buffer, bytes );
+
+ copied += bytes;
+ ringWritePosition += bytes;
+ ringBufferUsed += bytes;
+
+ if ( ringWritePosition == ringBuffer + ringBufferSize )
+ ringWritePosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::drain( Drainer *drainer, size_t max_bytes )
+{
+ // read to the end of the ring buffer
+ size_t used = ringBufferUsed;
+ size_t bytes = used;
+
+ bytes = MIN( bytes, max_bytes );
+
+ size_t copied = 0;
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+ size_t drain1 = MIN( end, bytes );
+
+ if ( !drain1 )
+ return 0;
+
+ size_t read1 = drainer->Write( ringReadPosition, drain1 );
+ if ( read1 == 0 )
+ return 0;
+
+ copied += read1;
+ ringReadPosition += read1;
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+
+ // update positions
+ ringBufferUsed -= read1;
+ bytes -= read1;
+
+ // see if we still have more to read after wrapping around
+ if ( drain1 == read1 && bytes )
+ {
+ size_t read2 = drainer->Write( ringReadPosition, bytes );
+
+ copied += read2;
+ ringReadPosition += read2;
+ ringBufferUsed -= read2;
+
+ if ( ringReadPosition == ringBuffer + ringBufferSize )
+ ringReadPosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::fill( Filler *filler, size_t max_bytes )
+{
+ // write to the end of the ring buffer
+ size_t used = ringBufferUsed;
+ size_t bytes = ringBufferSize - used;
+
+ bytes = MIN( bytes, max_bytes );
+
+ size_t copied = 0;
+ size_t end = ringBufferSize - ( ringWritePosition - ringBuffer );
+ size_t fill1 = MIN( end, bytes );
+
+ if ( !fill1 )
+ return 0;
+
+ size_t write1 = filler->Read( ringWritePosition, fill1 );
+ if ( write1 == 0 )
+ return 0;
+
+ copied += write1;
+ ringWritePosition += write1;
+
+ if ( ringWritePosition == ringBuffer + ringBufferSize )
+ ringWritePosition = ringBuffer;
+
+ // update positions
+ ringBufferUsed += write1;
+ bytes -= write1;
+
+ // see if we still have more to write after wrapping around
+ if ( fill1 == write1 && bytes )
+ {
+ size_t write2 = filler->Read( ringWritePosition, bytes );
+
+ copied += write2;
+ ringWritePosition += write2;
+ ringBufferUsed += write2;
+
+ if ( ringWritePosition == ringBuffer + ringBufferSize )
+ ringWritePosition = ringBuffer;
+ }
+
+ return copied;
+}
+
+size_t RingBuffer::size() const
+{
+ return ringBufferUsed;
+}
+
+void RingBuffer::clear()
+{
+ ringBufferUsed = 0;
+ ringWritePosition = ringBuffer;
+ ringReadPosition = ringBuffer;
+}
+
+void *RingBuffer::LockBuffer()
+{
+ return ringBuffer;
+}
+
+void RingBuffer::UnlockBuffer( size_t written )
+{
+ ringWritePosition = ringBuffer + written;
+ ringBufferUsed = written;
+}
+
+size_t RingBuffer::write_position() const
+{
+ return (size_t)ringWritePosition;
+}
+
+size_t RingBuffer::read_position() const
+{
+ return (size_t)ringReadPosition;
+}
+
+void RingBuffer::get_read_buffer( size_t bytes, const void **buffer, size_t *bytes_available ) const
+{
+ size_t toCopy = MIN( ringBufferUsed, bytes );
+
+ // read to the end of the ring buffer
+ size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
+
+ *bytes_available = MIN( end, toCopy );
+ *buffer = ringReadPosition;
+}
diff --git a/Src/replicant/nu/RingBuffer.h b/Src/replicant/nu/RingBuffer.h
new file mode 100644
index 000000000..bff147d19
--- /dev/null
+++ b/Src/replicant/nu/RingBuffer.h
@@ -0,0 +1,61 @@
+/*
+* Not Thread Safe !
+*/
+
+#pragma once
+#include <stddef.h>
+
+class Filler
+{
+public:
+ virtual size_t Read(void *dest, size_t len)=0;
+};
+
+class Drainer
+{
+public:
+ virtual size_t Write(const void *dest, size_t len)=0;
+};
+
+class RingBuffer
+{
+public:
+ RingBuffer();
+ ~RingBuffer();
+
+ void Reset();
+ bool reserve( size_t bytes ); // destructive.
+ int expand( size_t bytes ); // like reserve, but only expands upward. non-destructive. returns an NError
+ bool empty() const;
+ size_t avail() const; // how much available for writing
+ size_t size() const; // how much available for reading
+ void clear();
+ size_t read( void *dest, size_t len ); // returns amount actually read
+ size_t advance( size_t len ); // same as read() but doesn't write the data any where.
+ size_t peek( void *dest, size_t len ) const; // same as read() but doesn't advance the read pointer
+ size_t write( const void *src, size_t len );
+ size_t fill( Filler *filler, size_t max_bytes );
+ size_t drain( Drainer *drainer, size_t max_bytes );
+ size_t at( size_t offset, void *dest, size_t len ) const; // peeks() from offset. returns bytes read
+
+ size_t write_position() const; // returns an integer representing a write position
+ size_t read_position() const; // returns an integer representing the read position
+
+ void get_read_buffer( size_t bytes, const void **buffer, size_t *bytes_available ) const; /* returns a pointer that you can read data from, call advance() when you are done */
+ /* DO NOT USING THIS UNLESS YOU KNOW WHAT YOU'RE DOING
+ you should only use it when the ring buffer is empty
+ 1) call clear() beforehand - very important!
+ 2) call LockBuffer(), it'll give you a buffer
+ 3) call UnlockBufer() with how much you've written
+ 4) you catch the man
+ */
+ void *LockBuffer();
+ void UnlockBuffer( size_t written );
+
+private:
+ volatile size_t ringBufferUsed;
+ size_t ringBufferSize;
+ char *ringBuffer;
+ char *ringWritePosition;
+ char *ringReadPosition;
+};
diff --git a/Src/replicant/nu/SafeSize.h b/Src/replicant/nu/SafeSize.h
new file mode 100644
index 000000000..02c97a071
--- /dev/null
+++ b/Src/replicant/nu/SafeSize.h
@@ -0,0 +1,51 @@
+#pragma once
+
+class SafeSize
+{
+public:
+ SafeSize()
+ {
+ value = 0;
+ overflow = false;
+ }
+
+ void Add(size_t add)
+ {
+ if (!overflow)
+ {
+ value += add;
+ if (value < add)
+ overflow=true;
+ }
+ }
+
+ void AddN(size_t size, size_t count)
+ {
+ if (!overflow)
+ {
+ size_t total = size * count;
+ if (total < size)
+ {
+ overflow = true;
+ }
+ else
+ {
+ Add(total);
+ }
+ }
+ }
+
+ bool Overflowed() const
+ {
+ return overflow;
+ }
+
+ operator size_t ()
+ {
+ return value;
+ }
+
+private:
+ size_t value;
+ bool overflow;
+}; \ No newline at end of file
diff --git a/Src/replicant/nu/ThreadLoop.h b/Src/replicant/nu/ThreadLoop.h
new file mode 100644
index 000000000..b65b29ff3
--- /dev/null
+++ b/Src/replicant/nu/ThreadLoop.h
@@ -0,0 +1,16 @@
+#pragma once
+#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
+#include "android-armv7/ThreadLoop.h"
+#elif defined(__ANDROID__) && (defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__))
+#include "android-arm/ThreadLoop.h"
+#elif defined(__ANDROID__) && defined(__i386__)
+#include "android-x86/ThreadLoop.h"
+#elif defined(_WIN32)
+#include "win/ThreadLoop.h"
+#elif defined(__linux__)
+#include "linux/ThreadLoop.h"
+#elif defined(__APPLE__)
+#include "osx/ThreadLoop.h"
+#else
+#error port me!
+#endif
diff --git a/Src/replicant/nu/lfitem.c b/Src/replicant/nu/lfitem.c
new file mode 100644
index 000000000..4ece0251c
--- /dev/null
+++ b/Src/replicant/nu/lfitem.c
@@ -0,0 +1,25 @@
+#include "nu/lfitem.h"
+#include "foundation/atomics.h"
+
+void lfitem_init(lfitem_t item)
+{
+ item->item=0;
+}
+
+void *lfitem_get(lfitem_t item)
+{
+ return nx_atomic_swap_pointer(0, &(item->item));
+}
+
+void *lfitem_set(lfitem_t item, const void *value)
+{
+ return nx_atomic_swap_pointer((void *)value, &(item->item));
+}
+
+void *lfitem_set_if_zero(lfitem_t item, void *value)
+{
+ if (nx_atomic_cmpxchg_pointer(0, value, &(item->item)) == 0)
+ return 0;
+ else
+ return (void *)value;
+} \ No newline at end of file
diff --git a/Src/replicant/nu/lfitem.h b/Src/replicant/nu/lfitem.h
new file mode 100644
index 000000000..7bb8a9d33
--- /dev/null
+++ b/Src/replicant/nu/lfitem.h
@@ -0,0 +1,23 @@
+#pragma once
+
+/* This data structure holds one item, and returns you the old item when you replace it
+ it's sort of a "stack of 1" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct lfitem_struct_t
+ {
+ void * volatile item;
+ } lfitem_value_t, *lfitem_t;
+
+
+ void lfitem_init(lfitem_t item);
+ void *lfitem_get(lfitem_t item);
+ void *lfitem_set(lfitem_t item, const void *value);
+ void *lfitem_set_if_zero(lfitem_t item, void *value);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/lfmpscq.c b/Src/replicant/nu/lfmpscq.c
new file mode 100644
index 000000000..0f7efa731
--- /dev/null
+++ b/Src/replicant/nu/lfmpscq.c
@@ -0,0 +1,50 @@
+#include "lfmpscq.h"
+#include "foundation/atomics.h"
+
+void mpscq_init(mpscq_t* self)
+{
+ self->head = &self->stub;
+ self->tail = &self->stub;
+ self->stub.Next = 0;
+}
+
+int mpscq_push(mpscq_t *self, queue_node_t *n)
+{
+ queue_node_t *prev;
+ n->Next = 0;
+ prev = nx_atomic_swap_pointer(n, (void * volatile *)&self->head);
+ //(*)
+ prev->Next = n;
+ return prev!=&self->stub;
+}
+
+queue_node_t *mpscq_pop(mpscq_t *self)
+{
+ queue_node_t* tail = self->tail;
+ queue_node_t* next = tail->Next;
+ queue_node_t* head;
+ if (tail == &self->stub)
+ {
+ if (0 == next)
+ return 0;
+ self->tail = next;
+ tail = next;
+ next = next->Next;
+ }
+ if (next)
+ {
+ self->tail = next;
+ return tail;
+ }
+ head = self->head;
+ if (tail != head)
+ return (queue_node_t *)1;
+ mpscq_push(self, &self->stub);
+ next = tail->Next;
+ if (next)
+ {
+ self->tail = next;
+ return tail;
+ }
+ return 0;
+}
diff --git a/Src/replicant/nu/lfmpscq.h b/Src/replicant/nu/lfmpscq.h
new file mode 100644
index 000000000..c06f33311
--- /dev/null
+++ b/Src/replicant/nu/lfmpscq.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "queue_node.h"
+
+/* lock free unbounded multiple producer, single consumer queue */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mpscq_struct_t
+{
+ queue_node_t * volatile head;
+ queue_node_t *tail;
+ queue_node_t stub;
+} mpscq_t;
+
+#define MPSCQ_STATIC_INIT(self) {&self.stub, &self.stub, {0}}
+
+void mpscq_init(mpscq_t* self);
+int mpscq_push(mpscq_t *self, queue_node_t *n);
+queue_node_t *mpscq_pop(mpscq_t *self); /* returns (queue_node_t *)1 if the queue is busy */
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/lfringbuffer.c b/Src/replicant/nu/lfringbuffer.c
new file mode 100644
index 000000000..4c38575b3
--- /dev/null
+++ b/Src/replicant/nu/lfringbuffer.c
@@ -0,0 +1,158 @@
+#include "lfringbuffer.h"
+#include "foundation/error.h"
+#include <stdlib.h>
+
+typedef struct LFRingBufferHeader
+{
+ volatile size_t used; /* number of bytes written, in elements */
+ size_t size; /* in elements */
+ size_t write_position;
+ size_t read_position;
+} LFRingBufferHeader;
+
+typedef struct LFRingBuffer
+{
+ LFRingBufferHeader header;
+ float data[1];
+} LFRingBuffer;
+
+/* create a ring buffer with the desired number of elements */
+int lfringbuffer_create(lfringbuffer_t *out_ring_buffer, size_t number_of_elements)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)malloc(sizeof(LFRingBufferHeader) + sizeof(float) * number_of_elements);
+ if (!ring_buffer)
+ return NErr_OutOfMemory;
+
+ ring_buffer->header.used = 0;
+ ring_buffer->header.size = number_of_elements;
+ ring_buffer->header.write_position = 0;
+ ring_buffer->header.read_position = 0;
+ *out_ring_buffer = (lfringbuffer_t)ring_buffer;
+ return NErr_Success;
+}
+
+int lfringbuffer_destroy(lfringbuffer_t ring_buffer)
+{
+ free(ring_buffer);
+ return NErr_Success;
+}
+
+/* ----- Read functions ----- */
+/* get how many elements can currently be read */
+size_t lfringbuffer_read_available(lfringbuffer_t rb)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ return ring_buffer->header.used;
+}
+
+/* retrieve a pointer that can be read from.
+you might have to call this twice, because of ring buffer wraparound
+call lfringbuffer_read_update() when you are done */
+int lfringbuffer_read_get(lfringbuffer_t rb, size_t elements_requested, const float **out_buffer, size_t *elements_available)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ size_t end, to_copy;
+ int ret = NErr_Success;
+
+ /* can only read how many bytes we have available */
+ to_copy=ring_buffer->header.used;
+ if (to_copy > elements_requested)
+ {
+ to_copy = elements_requested;
+ ret = NErr_Underrun; /* signal that there was a buffer underrun when reading */
+ }
+
+ /* can only read until the end of the buffer */
+ end = ring_buffer->header.size-ring_buffer->header.read_position;
+ if (to_copy > end)
+ {
+ to_copy = end;
+ ret = NErr_TryAgain; /* signal that they need to call again to get the next part of the buffer */
+ }
+
+ *out_buffer = ring_buffer->data + ring_buffer->header.read_position;
+ *elements_available = to_copy;
+
+ ring_buffer->header.read_position += to_copy;
+ if (ring_buffer->header.read_position == ring_buffer->header.size)
+ ring_buffer->header.read_position=0;
+
+ return ret;
+}
+
+/* call to acknowledge that you have read the bytes */
+void lfringbuffer_read_update(lfringbuffer_t rb, size_t elements_read)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ nx_atomic_sub(elements_read, &ring_buffer->header.used);
+}
+static void lfringbuffer_read_set_position(lfringbuffer_t rb, size_t position)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ intptr_t bytes_to_flush = (intptr_t)(position - (size_t)ring_buffer->header.read_position);
+ if (bytes_to_flush < 0)
+ bytes_to_flush += ring_buffer->header.size;
+ lfringbuffer_read_update(rb, bytes_to_flush);
+}
+
+/* ----- Write functions ----- */
+/* get how many elements can currently be written */
+size_t lfringbuffer_write_available(lfringbuffer_t rb)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ return ring_buffer->header.size - ring_buffer->header.used;
+}
+
+/* retrieve a pointer that can be written to.
+you might have to call this twice, because of ring buffer wraparound
+call lfringbuffer_write_update() when you are done */
+int lfringbuffer_write_get(lfringbuffer_t rb, size_t elements_requested, float **out_buffer, size_t *elements_available)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ size_t end, to_copy;
+ int ret = NErr_Success;
+
+ /* can only write how many bytes we have available */
+ to_copy=ring_buffer->header.size - ring_buffer->header.used;
+ if (to_copy > elements_requested)
+ {
+ to_copy = elements_requested;
+ ret = NErr_Underrun; /* signal that there was a buffer underrun when reading */
+ }
+
+ /* can only read until the end of the buffer */
+ end = ring_buffer->header.size-ring_buffer->header.write_position;
+ if (to_copy > end)
+ {
+ to_copy = end;
+ ret = NErr_TryAgain; /* signal that they need to call again to get the next part of the buffer */
+ }
+
+ *out_buffer = ring_buffer->data + ring_buffer->header.write_position;
+ *elements_available = to_copy;
+
+ ring_buffer->header.write_position += to_copy;
+ if (ring_buffer->header.write_position == ring_buffer->header.size)
+ ring_buffer->header.write_position=0;
+
+ return ret;
+}
+
+/* call to acknowledge that you have written the bytes */
+void lfringbuffer_write_update(lfringbuffer_t rb, size_t elements_read)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+#if defined(__ARM_ARCH_7A__)
+ __asm__ __volatile__ ("dmb" : : : "memory");
+#endif
+ nx_atomic_add(elements_read, &ring_buffer->header.used);
+}
+
+size_t lfringbuffer_write_get_position(lfringbuffer_t rb)
+{
+ LFRingBuffer *ring_buffer = (LFRingBuffer *)rb;
+ return ring_buffer->header.write_position;
+}
diff --git a/Src/replicant/nu/lfringbuffer.h b/Src/replicant/nu/lfringbuffer.h
new file mode 100644
index 000000000..03b3804b2
--- /dev/null
+++ b/Src/replicant/nu/lfringbuffer.h
@@ -0,0 +1,46 @@
+#pragma once
+#include "foundation/types.h"
+/* lock free floating point ring buffer
+ generic implementation
+
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct lfringbuffer_s { } *lfringbuffer_t;
+
+ /* create a ring buffer with the desired number of elements */
+ int lfringbuffer_create(lfringbuffer_t *out_ring_buffer, size_t number_of_elements);
+ int lfringbuffer_destroy(lfringbuffer_t ring_buffer);
+
+ /* ----- Read functions ----- */
+ /* get how many elements can currently be read */
+ size_t lfringbuffer_read_available(lfringbuffer_t ring_buffer);
+
+ /* retrieve a pointer that can be read from.
+ you might have to call this twice, because of ring buffer wraparound
+ call lfringbuffer_read_update() when you are done */
+ int lfringbuffer_read_get(lfringbuffer_t ring_buffer, size_t elements_requested, const float **out_buffer, size_t *elements_available);
+
+ /* call to acknowledge that you have read the bytes */
+ void lfringbuffer_read_update(lfringbuffer_t ring_buffer, size_t elements_read);
+
+ /* ----- Write functions ----- */
+ /* get how many elements can currently be written */
+ size_t lfringbuffer_write_available(lfringbuffer_t ring_buffer);
+
+ /* retrieve a pointer that can be written to.
+ you might have to call this twice, because of ring buffer wraparound
+ call lfringbuffer_write_update() when you are done */
+ int lfringbuffer_write_get(lfringbuffer_t ring_buffer, size_t elements_requested, float **out_buffer, size_t *elements_available);
+
+ /* call to acknowledge that you have written the bytes */
+ void lfringbuffer_write_update(lfringbuffer_t ring_buffer, size_t elements_read);
+
+ size_t lfringbuffer_write_get_position(lfringbuffer_t ring_buffer);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/nodelist.c b/Src/replicant/nu/nodelist.c
new file mode 100644
index 000000000..f1c21ea40
--- /dev/null
+++ b/Src/replicant/nu/nodelist.c
@@ -0,0 +1,71 @@
+#include "nodelist.h"
+
+void nodelist_init(nodelist_t nodelist)
+{
+ nodelist->head=0;
+ nodelist->tail=0;
+}
+
+void nodelist_push_back(nodelist_t nodelist, queue_node_t *item)
+{
+ if (!nodelist->head)
+ {
+ nodelist->head=item;
+ nodelist->tail=item;
+ }
+ else
+ {
+ nodelist->tail->Next=item;
+ nodelist->tail=item;
+ }
+ item->Next = 0;
+}
+
+void nodelist_push_front(nodelist_t nodelist, queue_node_t *item)
+{
+ if (!nodelist->head)
+ {
+ nodelist->head=item;
+ nodelist->tail=item;
+ item->Next=0;
+ }
+ else
+ {
+ item->Next = nodelist->head;
+ nodelist->head = item;
+ }
+}
+
+queue_node_t *nodelist_pop_front(nodelist_t nodelist)
+{
+ queue_node_t *ret;
+ if (!nodelist->head)
+ return 0;
+
+ ret = nodelist->head;
+ nodelist->head = nodelist->head->Next;
+ ret->Next = 0; // so we don't confuse anyone
+
+ if (ret == nodelist->tail)
+ {
+ nodelist->tail = 0;
+ }
+ return ret;
+}
+
+void nodelist_push_back_list(nodelist_t nodelist, queue_node_t *item)
+{
+ if (!nodelist->head)
+ {
+ nodelist->head=item;
+ }
+ else
+ {
+ nodelist->tail->Next=item;
+ }
+
+ while (item->Next)
+ item = item->Next;
+
+ nodelist->tail = item;
+}
diff --git a/Src/replicant/nu/nodelist.h b/Src/replicant/nu/nodelist.h
new file mode 100644
index 000000000..5a7f1ec1a
--- /dev/null
+++ b/Src/replicant/nu/nodelist.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "queue_node.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // non-thread-safe list of queue_node_t objects with _head and _tail
+ typedef struct nodelist_s
+ {
+ queue_node_t *head;
+ queue_node_t *tail;
+ } nodelist_s, *nodelist_t;
+
+ void nodelist_init(nodelist_t nodelist);
+ void nodelist_push_back(nodelist_t nodelist, queue_node_t *item);
+ void nodelist_push_front(nodelist_t nodelist, queue_node_t *item);
+ queue_node_t *nodelist_pop_front(nodelist_t nodelist);
+ // pushes an item onto the list, but treat it as a whole list rather than a single item
+ void nodelist_push_back_list(nodelist_t nodelist, queue_node_t *item);
+
+
+ #ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/ns_wc.h b/Src/replicant/nu/ns_wc.h
new file mode 100644
index 000000000..0520f7dad
--- /dev/null
+++ b/Src/replicant/nu/ns_wc.h
@@ -0,0 +1,51 @@
+#pragma once
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+__inline int MultiByteToWideCharSZ(
+ UINT CodePage, // code page
+ DWORD dwFlags, // character-type options
+ LPCSTR lpMultiByteStr, // string to map
+ int cbMultiByte, // number of bytes in string
+ LPWSTR lpWideCharStr, // wide-character buffer
+ int cchWideChar // size of buffer
+)
+{
+ int converted=0;
+ if (cchWideChar == 0)
+ return MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
+ converted = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar-1);
+ if (!converted)
+ return 0;
+ lpWideCharStr[converted]=0;
+ return converted+1;
+}
+
+__inline int WideCharToMultiByteSZ(
+ UINT CodePage, // code page
+ DWORD dwFlags, // performance and mapping flags
+ LPCWSTR lpWideCharStr, // wide-character string
+ int cchWideChar, // number of chars in string
+ LPSTR lpMultiByteStr, // buffer for new string
+ int cbMultiByte, // size of buffer
+ LPCSTR lpDefaultChar, // default for unmappable chars
+ LPBOOL lpUsedDefaultChar // set when default char used
+)
+{
+ int converted=0;
+ if (cbMultiByte == 0)
+ return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
+ converted= WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte-1, lpDefaultChar, lpUsedDefaultChar);
+ if (!converted)
+ return converted;
+ lpMultiByteStr[converted]=0;
+ return converted+1;
+}
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nu/nu.sln b/Src/replicant/nu/nu.sln
new file mode 100644
index 000000000..f7b3dc6fb
--- /dev/null
+++ b/Src/replicant/nu/nu.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nu/nu.vcxproj b/Src/replicant/nu/nu.vcxproj
new file mode 100644
index 000000000..25e634f8b
--- /dev/null
+++ b/Src/replicant/nu/nu.vcxproj
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}</ProjectGuid>
+ <RootNamespace>nu</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="BitReader.cpp" />
+ <ClCompile Include="ByteReader.c" />
+ <ClCompile Include="ByteWriter.c" />
+ <ClCompile Include="lfitem.c" />
+ <ClCompile Include="lfmpscq.c" />
+ <ClCompile Include="LockFreeFIFO.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="LockFreeRingBuffer.cpp" />
+ <ClCompile Include="nodelist.c" />
+ <ClCompile Include="ProgressTracker.cpp" />
+ <ClCompile Include="RingBuffer.cpp" />
+ <ClCompile Include="win-amd64\ThreadLoop.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="win-x86\LockFreeLIFO.c">
+ <AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerListingLocation Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)x86_Release\lifo32.asm</AssemblerListingLocation>
+ <AssemblerListingLocation Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)x64_Release\lifo32.asm</AssemblerListingLocation>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="win\MessageLoop.cpp" />
+ <ClCompile Include="win\ThreadLoop.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="BitReader.h" />
+ <ClInclude Include="ByteWriter.h" />
+ <ClInclude Include="lfmpscq.h" />
+ <ClInclude Include="LockFreeFIFO.h" />
+ <ClInclude Include="LockFreeItem.h" />
+ <ClInclude Include="LockFreeLIFO.h" />
+ <ClInclude Include="LockFreeRingBuffer.h" />
+ <ClInclude Include="nodelist.h" />
+ <ClInclude Include="ProgressTracker.h" />
+ <ClInclude Include="queue_node.h" />
+ <ClInclude Include="win-amd64\LockFreeLIFO.h" />
+ <ClInclude Include="win-amd64\ThreadLoop.h" />
+ <ClInclude Include="win-x86\LockFreeLIFO.h" />
+ <ClInclude Include="win\ThreadLoop.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="win-x86\LockFreeLIFO.asm">
+ <FileType>Document</FileType>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">ml /c /nologo /Fo"$(ProjectDir)x86_Debug\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">ml /c /nologo /Fo"$(ProjectDir)x64_Debug\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)x86_Debug\%(Filename)-$(PlatformShortName).obj</Outputs>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)x64_Debug\%(Filename)-$(PlatformShortName).obj</Outputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(FullPath)</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(FullPath)</AdditionalInputs>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ml /c /nologo /Fo"$(ProjectDir)x86_Release\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ml /c /nologo /Fo"$(ProjectDir)x64_Release\%(Filename)-$(PlatformShortName).obj" /Zi %(FullPath)</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)x86_Release\%(Filename)-$(PlatformShortName).obj</Outputs>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)x64_Release\%(Filename)-$(PlatformShortName).obj</Outputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(FullPath)</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(FullPath)</AdditionalInputs>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nu/nu.vcxproj.filters b/Src/replicant/nu/nu.vcxproj.filters
new file mode 100644
index 000000000..cdb52bfcf
--- /dev/null
+++ b/Src/replicant/nu/nu.vcxproj.filters
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="BitReader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ByteReader.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ByteWriter.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="lfitem.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="lfmpscq.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="LockFreeFIFO.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win-x86\LockFreeLIFO.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="LockFreeRingBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\MessageLoop.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="nodelist.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ProgressTracker.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RingBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win-amd64\ThreadLoop.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\ThreadLoop.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="win-amd64\ThreadLoop.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="win\ThreadLoop.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="queue_node.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ProgressTracker.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="nodelist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="LockFreeRingBuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="LockFreeLIFO.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="win-x86\LockFreeLIFO.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="win-amd64\LockFreeLIFO.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="LockFreeItem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="LockFreeFIFO.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="lfmpscq.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ByteWriter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="BitReader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{600469f6-bb91-4a1c-9603-9036bf14452f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{c7911d31-e53d-45f9-b477-140f1e534e28}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{5c814c31-6040-46c7-9e53-e472177be0f1}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="win-x86\LockFreeLIFO.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nu/precomp.h b/Src/replicant/nu/precomp.h
new file mode 100644
index 000000000..f19fd91ac
--- /dev/null
+++ b/Src/replicant/nu/precomp.h
@@ -0,0 +1,18 @@
+//
+// precomp.h
+// nu
+//
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+#include "foundation/atomics.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif //WIN32
diff --git a/Src/replicant/nu/queue_node.h b/Src/replicant/nu/queue_node.h
new file mode 100644
index 000000000..884780703
--- /dev/null
+++ b/Src/replicant/nu/queue_node.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef SLIST_ENTRY queue_node_t;
+#ifdef __cplusplus
+}
+#endif
+
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct queue_node_struct_t
+{
+ struct queue_node_struct_t *Next;
+} queue_node_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Src/replicant/nu/strsafe.h b/Src/replicant/nu/strsafe.h
new file mode 100644
index 000000000..3c94fe405
--- /dev/null
+++ b/Src/replicant/nu/strsafe.h
@@ -0,0 +1,4497 @@
+/******************************************************************
+* *
+* strsafe.h -- This module defines safer C library string *
+* routine replacements. These are meant to make C *
+* a bit more safe in reference to security and *
+* robustness *
+* *
+* Copyright (c) Microsoft Corp. All rights reserved. *
+* Ported to Unix by Ben Allison - Nullsoft, Inc. *
+* *
+******************************************************************/
+#pragma once
+#ifdef _WIN32
+#include <strsafe.h>
+#else
+#pragma once
+
+#include <stdio.h> // for _vsnprintf, _vsnwprintf, getc, getwc
+#include <string.h> // for memset
+#include <stdarg.h> // for va_start, etc.
+#include <wchar.h>
+
+
+#include <stdint.h>
+
+#ifndef _HRESULT_DEFINED
+#define _HRESULT_DEFINED
+typedef long HRESULT;
+#endif // !_HRESULT_DEFINED
+
+#ifndef SUCCEEDED
+#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
+#endif
+
+#ifndef FAILED
+#define FAILED(hr) ((HRESULT)(hr) < 0)
+#endif
+
+#ifndef S_OK
+#define S_OK ((HRESULT)0x00000000L)
+#endif
+
+#ifdef __cplusplus
+#define _STRSAFE_EXTERN_C extern "C"
+#else
+#define _STRSAFE_EXTERN_C extern
+#endif
+
+// If you do not want to use these functions inline (and instead want to link w/ strsafe.lib), then
+// #define STRSAFE_LIB before including this header file.
+#ifndef _WIN32
+#define __inline inline
+#define __stdcall
+#endif
+
+#if defined(STRSAFE_LIB)
+#define STRSAFEAPI _STRSAFE_EXTERN_C HRESULT __stdcall
+#pragma comment(lib, "./strsafe.lib")
+#elif defined(STRSAFE_LIB_IMPL)
+#define STRSAFEAPI _STRSAFE_EXTERN_C __declspec(dllexport) HRESULT __stdcall
+#else
+#define STRSAFEAPI __inline HRESULT __stdcall
+#define STRSAFE_INLINE
+#endif
+
+// Some functions always run inline because they use stdin and we want to avoid building multiple
+// versions of strsafe lib depending on if you use msvcrt, libcmt, etc.
+#define STRSAFE_INLINE_API __inline HRESULT __stdcall
+
+// The user can request no "Cb" or no "Cch" fuctions, but not both!
+#if defined(STRSAFE_NO_CB_FUNCTIONS) && defined(STRSAFE_NO_CCH_FUNCTIONS)
+#error cannot specify both STRSAFE_NO_CB_FUNCTIONS and STRSAFE_NO_CCH_FUNCTIONS !!
+#endif
+
+// This should only be defined when we are building strsafe.lib
+#ifdef STRSAFE_LIB_IMPL
+#define STRSAFE_INLINE
+#endif
+
+
+// If both strsafe.h and ntstrsafe.h are included, only use definitions from one.
+#ifndef _NTSTRSAFE_H_INCLUDED_
+
+#define STRSAFE_MAX_CCH 2147483647 // max # of characters we support (same as INT_MAX)
+
+// Flags for controling the Ex functions
+//
+// STRSAFE_FILL_BYTE(0xFF) 0x000000FF // bottom byte specifies fill pattern
+#define STRSAFE_IGNORE_NULLS 0x00000100 // treat null as TEXT("") -- don't fault on NULL buffers
+#define STRSAFE_FILL_BEHIND_NULL 0x00000200 // fill in extra space behind the null terminator
+#define STRSAFE_FILL_ON_FAILURE 0x00000400 // on failure, overwrite pszDest with fill pattern and null terminate it
+#define STRSAFE_NULL_ON_FAILURE 0x00000800 // on failure, set *pszDest = TEXT('\0')
+#define STRSAFE_NO_TRUNCATION 0x00001000 // instead of returning a truncated result, copy/append nothing to pszDest and null terminate it
+
+#define STRSAFE_VALID_FLAGS (0x000000FF | STRSAFE_IGNORE_NULLS | STRSAFE_FILL_BEHIND_NULL | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)
+
+// helper macro to set the fill character and specify buffer filling
+#define STRSAFE_FILL_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_BEHIND_NULL))
+#define STRSAFE_FAILURE_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_ON_FAILURE))
+
+#define STRSAFE_GET_FILL_PATTERN(dwFlags) ((int)(dwFlags & 0x000000FF))
+
+#endif // _NTSTRSAFE_H_INCLUDED_
+
+// STRSAFE error return codes
+//
+#define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007AL) // 0x7A = 122L = ERROR_INSUFFICIENT_BUFFER
+#define STRSAFE_E_INVALID_PARAMETER ((HRESULT)0x80070057L) // 0x57 = 87L = ERROR_INVALID_PARAMETER
+#define STRSAFE_E_END_OF_FILE ((HRESULT)0x80070026L) // 0x26 = 38L = ERROR_HANDLE_EOF
+
+// prototypes for the worker functions
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc);
+STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc);
+STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc);
+STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend);
+STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList);
+STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
+STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch);
+#endif // STRSAFE_INLINE
+
+#ifndef STRSAFE_LIB_IMPL
+// these functions are always inline
+STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#endif
+
+#ifdef _NTSTRSAFE_H_INCLUDED_
+#pragma warning(push)
+#pragma warning(disable : 4995)
+#endif // _NTSTRSAFE_H_INCLUDED_
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopy(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for strncpy. That function will pad the
+ destination string with extra null termination characters if the count is
+ greater than the length of the source string, and it will fail to null
+ terminate the destination string if the source string length is greater
+ than or equal to the count. You can not blindly use this instead of strncpy:
+ it is common for code to use it to "patch" strings and you would introduce
+ errors if the code started null terminating in the middle of the string.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was copied without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of
+ pszSrc will be copied to pszDest as possible, and pszDest will be null
+ terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(src) + 1) to hold all of the
+ source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCopyEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc);
+#define StringCchCopy StringCchCopyA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopy(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy'.
+ The size of the destination buffer (in bytes) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for strncpy. That function will pad the
+ destination string with extra null termination characters if the count is
+ greater than the length of the source string, and it will fail to null
+ terminate the destination string if the source string length is greater
+ than or equal to the count. You can not blindly use this instead of strncpy:
+ it is common for code to use it to "patch" strings and you would introduce
+ errors if the code started null terminating in the middle of the string.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was copied without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be copied to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc);
+#define StringCbCopy StringCbCopyA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopyEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCchCopy, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(pszSrc) + 1) to hold all of
+ the source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#define StringCchCopyEx StringCchCopyExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopyEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCbCopy, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcbRemaining - pcbRemaining is non-null,the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#define StringCbCopyEx StringCbCopyExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopyN(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cchSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cchSrc is greater than the length of pszSrc.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the entire string or the first cchSrc characters were copied
+ without truncation and the resultant destination string was null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be copied to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(src) + 1) to hold all of the
+ source including the null terminator
+
+ pszSrc - source string
+
+ cchSrc - maximum number of characters to copy from source string,
+ not including the null terminator.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCopyNEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc);
+#define StringCchCopyN StringCchCopyNA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc)
+{
+ HRESULT hr;
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopyN(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cbSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy'.
+ The size of the destination buffer (in bytes) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cbSrc is greater than the size of pszSrc.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the entire string or the first cbSrc characters were
+ copied without truncation and the resultant destination string was null
+ terminated, otherwise it will return a failure code. In failure cases as
+ much of pszSrc will be copied to pszDest as possible, and pszDest will be
+ null terminated.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string
+
+ cbSrc - maximum number of bytes to copy from source string,
+ not including the null terminator.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc);
+#define StringCbCopyN StringCbCopyNA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchSrc;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(char);
+ cchSrc = cbSrc / sizeof(char);
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCopyNEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cchSrc,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCchCopyN, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination
+ string including the null terminator. The flags parameter allows
+ additional controls.
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cchSrc is greater than the length of pszSrc.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(pszSrc) + 1) to hold all of
+ the source including the null terminator
+
+ pszSrc - source string
+
+ cchSrc - maximum number of characters to copy from the source
+ string
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#define StringCchCopyNEx StringCchCopyNExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCopyNEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cbSrc,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncpy' with
+ some additional parameters. In addition to functionality provided by
+ StringCbCopyN, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+ This routine is meant as a replacement for strncpy, but it does behave
+ differently. This function will not pad the destination buffer with extra
+ null termination characters if cbSrc is greater than the size of pszSrc.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to
+ hold all of the source including the null terminator
+
+ pszSrc - source string
+
+ cbSrc - maximum number of bytes to copy from source string
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcbRemaining - pcbRemaining is non-null,the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcpy
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all copied and the
+ resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the copy
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#define StringCbCopyNEx StringCbCopyNExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchSrc;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+ cchSrc = cbSrc / sizeof(char);
+
+ if ((cchDest > STRSAFE_MAX_CCH) ||
+ (cchSrc > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCat(
+ IN OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat'.
+ The size of the destination buffer (in characters) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was concatenated without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be appended to pszDest as possible, and pszDest will be null
+ terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters.
+ length must be = (_tcslen(pszDest) + _tcslen(pszSrc) + 1)
+ to hold all of the combine string plus the null
+ terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCatEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error occurs,
+ the destination buffer is modified to contain a truncated
+ version of the ideal result and is null terminated. This
+ is useful for situations where truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc);
+#define StringCchCat StringCchCatA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCat(
+ IN OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat'.
+ The size of the destination buffer (in bytes) is a parameter and this
+ function will not write past the end of this buffer and it will ALWAYS
+ null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was concatenated without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases as much of pszSrc
+ will be appended to pszDest as possible, and pszDest will be null
+ terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be = ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator
+
+ pszSrc - source string which must be null terminated
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCatEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error occurs,
+ the destination buffer is modified to contain a truncated
+ version of the ideal result and is null terminated. This
+ is useful for situations where truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc);
+#define StringCbCat StringCbCatA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatWorkerA(pszDest, cchDest, pszSrc);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCatEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat' with
+ some additional parameters. In addition to functionality provided by
+ StringCchCat, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters
+ length must be (_tcslen(pszDest) + _tcslen(pszSrc) + 1)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcat
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#define StringCchCatEx StringCchCatExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCatEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strcat' with
+ some additional parameters. In addition to functionality provided by
+ StringCbCat, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string which must be null terminated
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return
+ the number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+ this flag is useful for emulating functions like lstrcat
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated
+ and the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#define StringCbCatEx StringCbCatExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCatN(
+ IN OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cchMaxAppend
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat'.
+ The size of the destination buffer (in characters) is a parameter as well as
+ the maximum number of characters to append, excluding the null terminator.
+ This function will not write past the end of the destination buffer and it will
+ ALWAYS null terminate pszDest (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if all of pszSrc or the first cchMaxAppend characters were appended
+ to the destination string and it was null terminated, otherwise it will
+ return a failure code. In failure cases as much of pszSrc will be appended
+ to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters.
+ length must be (_tcslen(pszDest) + min(cchMaxAppend, _tcslen(pszSrc)) + 1)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cchMaxAppend - maximum number of characters to append
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCchCatNEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cchMaxAppend characters
+ were concatenated to pszDest and the resultant dest
+ string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend);
+#define StringCchCatN StringCchCatNA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCatN(
+ IN OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc,
+ IN size_t cbMaxAppend
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat'.
+ The size of the destination buffer (in bytes) is a parameter as well as
+ the maximum number of bytes to append, excluding the null terminator.
+ This function will not write past the end of the destination buffer and it will
+ ALWAYS null terminate pszDest (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if all of pszSrc or the first cbMaxAppend bytes were appended
+ to the destination string and it was null terminated, otherwise it will
+ return a failure code. In failure cases as much of pszSrc will be appended
+ to pszDest as possible, and pszDest will be null terminated.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszDest) + min(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cbMaxAppend - maximum number of bytes to append
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL. See StringCbCatNEx if you require
+ the handling of NULL values.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cbMaxAppend bytes were
+ concatenated to pszDest and the resultant dest string
+ was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend);
+
+#define StringCbCatN StringCbCatNA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchMaxAppend;
+
+ cchMaxAppend = cbMaxAppend / sizeof(char);
+
+ hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchCatNEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cchMaxAppend,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat', with
+ some additional parameters. In addition to functionality provided by
+ StringCchCatN, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cchDest - size of destination buffer in characters.
+ length must be (_tcslen(pszDest) + min(cchMaxAppend, _tcslen(pszSrc)) + 1)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cchMaxAppend - maximum number of characters to append
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cchMaxAppend characters
+ were concatenated to pszDest and the resultant dest
+ string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#define StringCchCatNEx StringCchCatNExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbCatNEx(
+ IN OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ IN LPCTSTR pszSrc OPTIONAL,
+ IN size_t cbMaxAppend,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strncat', with
+ some additional parameters. In addition to functionality provided by
+ StringCbCatN, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string which must be null terminated
+
+ cbDest - size of destination buffer in bytes.
+ length must be ((_tcslen(pszDest) + min(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
+ to hold all of the combine string plus the null
+ terminator.
+
+ pszSrc - source string
+
+ cbMaxAppend - maximum number of bytes to append
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function appended any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any pre-existing
+ or truncated string
+
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any pre-existing or
+ truncated string
+
+ STRSAFE_NO_TRUNCATION
+ if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
+ will not contain a truncated string, it will remain unchanged.
+
+Notes:
+ Behavior is undefined if source and destination strings overlap.
+
+ pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
+ is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
+ may be NULL. An error may still be returned even though NULLS are ignored
+ due to insufficient space.
+
+Return Value:
+
+ S_OK - if all of pszSrc or the first cbMaxAppend bytes were
+ concatenated to pszDest and the resultant dest string
+ was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the operation
+ failed due to insufficient space. When this error
+ occurs, the destination buffer is modified to contain
+ a truncated version of the ideal result and is null
+ terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
+#define StringCbCatNEx StringCbCatNExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchMaxAppend;
+
+ cchMaxAppend = cbMaxAppend / sizeof(char);
+
+ hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchVPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszFormat,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCchVPrintfEx if you
+ require the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList);
+#define StringCchVPrintf StringCchVPrintfA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbVPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszFormat,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf'.
+ The size of the destination buffer (in bytes) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCbVPrintfEx if you
+ require the handling of NULL values.
+
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList);
+#define StringCbVPrintf StringCbVPrintfA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest,
+ IN LPCTSTR pszFormat,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCchPrintfEx if you
+ require the handling of NULL values.
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...);
+#define StringCchPrintf StringCchPrintfA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbPrintf(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest,
+ IN LPCTSTR pszFormat,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf'.
+ The size of the destination buffer (in bytes) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string was printed without truncation and null terminated,
+ otherwise it will return a failure code. In failure cases it will return
+ a truncated version of the ideal result.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes
+ length must be sufficient to hold the resulting formatted
+ string, including the null terminator.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL. See StringCbPrintfEx if you
+ require the handling of NULL values.
+
+
+Return Value:
+
+ S_OK - if there was sufficient space in the dest buffer for
+ the resultant string and it was null terminated.
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...);
+#define StringCbPrintf StringCbPrintfA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCchPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return
+ the number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...);
+#define StringCchPrintfEx StringCchPrintfExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+ va_list argList;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ ...
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'sprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCbPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of bytes left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return
+ the number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ ... - additional parameters to be formatted according to
+ the format string
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...);
+#define StringCbPrintfEx StringCbPrintfExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ va_list argList;
+
+ va_start(argList, pszFormat);
+
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
+
+ va_end(argList);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchVPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCchVPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return
+ the number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
+#define StringCchVPrintfEx StringCchVPrintfExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
+ }
+
+ return hr;
+}
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbVPrintfEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags,
+ IN LPCTSTR pszFormat OPTIONAL,
+ IN va_list argList
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'vsprintf' with
+ some additional parameters. In addition to functionality provided by
+ StringCbVPrintf, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+ length must be sufficient to contain the resulting
+ formatted string plus the null terminator.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return
+ a pointer to the end of the destination string. If the
+ function printed any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pcbRemaining is non-null, the function will return
+ the number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT(""))
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated. This will overwrite any truncated
+ string returned when the failure is
+ STRSAFE_E_INSUFFICIENT_BUFFER
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string. This will overwrite any truncated string
+ returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
+
+ pszFormat - format string which must be null terminated
+
+ argList - va_list from the variable arguments according to the
+ stdarg.h convention
+
+Notes:
+ Behavior is undefined if destination, format strings or any arguments
+ strings overlap.
+
+ pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
+ flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
+ pszFormat may be NULL. An error may still be returned even though NULLS
+ are ignored due to insufficient space.
+
+Return Value:
+
+ S_OK - if there was source data and it was all concatenated and
+ the resultant dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that the print
+ operation failed due to insufficient space. When this
+ error occurs, the destination buffer is modified to
+ contain a truncated version of the ideal result and is
+ null terminated. This is useful for situations where
+ truncation is ok.
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function
+
+--*/
+
+STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
+#define StringCbVPrintfEx StringCbVPrintfExA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchGets(
+ OUT LPTSTR pszDest,
+ IN size_t cchDest
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets'.
+ The size of the destination buffer (in characters) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for fgets. That function does not replace
+ newline characters with a null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if any characters were read from stdin and copied to pszDest and
+ pszDest was null terminated, otherwise it will return a failure code.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+
+Notes:
+ pszDest should not be NULL. See StringCchGetsEx if you require the handling
+ of NULL values.
+
+ cchDest must be > 1 for this function to succeed.
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest);
+#define StringCchGets StringCchGetsA
+
+STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0);
+ }
+
+ return hr;
+}
+
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbGets(
+ OUT LPTSTR pszDest,
+ IN size_t cbDest
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets'.
+ The size of the destination buffer (in bytes) is a parameter and
+ this function will not write past the end of this buffer and it will
+ ALWAYS null terminate the destination buffer (unless it is zero length).
+
+ This routine is not a replacement for fgets. That function does not replace
+ newline characters with a null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if any characters were read from stdin and copied to pszDest
+ and pszDest was null terminated, otherwise it will return a failure code.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+
+Notes:
+ pszDest should not be NULL. See StringCbGetsEx if you require the handling
+ of NULL values.
+
+ cbDest must be > sizeof(TCHAR) for this function to succeed.
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest);
+
+#define StringCbGets StringCbGetsA
+
+STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest)
+{
+ HRESULT hr;
+ size_t cchDest;
+
+ // convert to count of characters
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0);
+ }
+
+ return hr;
+}
+
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchGetsEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cchDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcchRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets' with
+ some additional parameters. In addition to functionality provided by
+ StringCchGets, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cchDest - size of destination buffer in characters.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcchRemaining - if pcchRemaining is non-null, the function will return the
+ number of characters left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated.
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string.
+
+Notes:
+ pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified.
+ If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be
+ returned even though NULLS are ignored
+
+ cchDest must be > 1 for this function to succeed.
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
+#define StringCchGetsEx StringCchGetsExA
+
+STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cbDest;
+
+ // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
+ cbDest = cchDest * sizeof(char);
+
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags);
+ }
+
+ return hr;
+}
+
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbGetsEx(
+ OUT LPTSTR pszDest OPTIONAL,
+ IN size_t cbDest,
+ OUT LPTSTR* ppszDestEnd OPTIONAL,
+ OUT size_t* pcbRemaining OPTIONAL,
+ IN DWORD dwFlags
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'gets' with
+ some additional parameters. In addition to functionality provided by
+ StringCbGets, this routine also returns a pointer to the end of the
+ destination string and the number of characters left in the destination string
+ including the null terminator. The flags parameter allows additional controls.
+
+Arguments:
+
+ pszDest - destination string
+
+ cbDest - size of destination buffer in bytes.
+
+ ppszDestEnd - if ppszDestEnd is non-null, the function will return a
+ pointer to the end of the destination string. If the
+ function copied any data, the result will point to the
+ null termination character
+
+ pcbRemaining - if pbRemaining is non-null, the function will return the
+ number of bytes left in the destination string,
+ including the null terminator
+
+ dwFlags - controls some details of the string copy:
+
+ STRSAFE_FILL_BEHIND_NULL
+ if the function succeeds, the low byte of dwFlags will be
+ used to fill the uninitialize part of destination buffer
+ behind the null terminator
+
+ STRSAFE_IGNORE_NULLS
+ treat NULL string pointers like empty strings (TEXT("")).
+
+ STRSAFE_FILL_ON_FAILURE
+ if the function fails, the low byte of dwFlags will be
+ used to fill all of the destination buffer, and it will
+ be null terminated.
+
+ STRSAFE_NO_TRUNCATION /
+ STRSAFE_NULL_ON_FAILURE
+ if the function fails, the destination buffer will be set
+ to the empty string.
+
+Notes:
+ pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified.
+ If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be
+ returned even though NULLS are ignored
+
+ cbDest must be > sizeof(TCHAR) for this function to succeed
+
+Return Value:
+
+ S_OK - data was read from stdin and copied, and the resultant
+ dest string was null terminated
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ STRSAFE_E_END_OF_FILE /
+ HRESULT_CODE(hr) == ERROR_HANDLE_EOF
+ - this return value indicates an error or end-of-file
+ condition, use feof or ferror to determine which one has
+ occured.
+
+ STRSAFE_E_INSUFFICIENT_BUFFER /
+ HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
+ - this return value is an indication that there was
+ insufficient space in the destination buffer to copy any
+ data
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pbRemaining, unsigned long dwFlags);
+#define StringCbGetsEx StringCbGetsExA
+
+STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
+{
+ HRESULT hr;
+ size_t cchDest;
+ size_t cchRemaining = 0;
+
+ cchDest = cbDest / sizeof(char);
+
+ if (cchDest > STRSAFE_MAX_CCH)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags);
+ }
+
+ if (SUCCEEDED(hr) ||
+ (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
+ (hr == STRSAFE_E_END_OF_FILE))
+ {
+ if (pcbRemaining)
+ {
+ // safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
+ }
+ }
+
+ return hr;
+}
+
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+#endif // !STRSAFE_LIB_IMPL
+
+#ifndef STRSAFE_NO_CCH_FUNCTIONS
+/*++
+
+STDAPI
+StringCchLength(
+ IN LPCTSTR psz,
+ IN size_t cchMax,
+ OUT size_t* pcch OPTIONAL
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strlen'.
+ It is used to make sure a string is not larger than a given length, and
+ it optionally returns the current length in characters not including
+ the null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string is non-null and the length including the null
+ terminator is less than or equal to cchMax characters.
+
+Arguments:
+
+ psz - string to check the length of
+
+ cchMax - maximum number of characters including the null terminator
+ that psz is allowed to contain
+
+ pcch - if the function succeeds and pcch is non-null, the current length
+ in characters of psz excluding the null terminator will be returned.
+ This out parameter is equivalent to the return value of strlen(psz)
+
+Notes:
+ psz can be null but the function will fail
+
+ cchMax should be greater than zero or the function will fail
+
+Return Value:
+
+ S_OK - psz is non-null and the length including the null
+ terminator is less than or equal to cchMax characters
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch);
+#define StringCchLength StringCchLengthA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch)
+{
+ HRESULT hr;
+
+ if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringLengthWorkerA(psz, cchMax, pcch);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CCH_FUNCTIONS
+
+
+#ifndef STRSAFE_NO_CB_FUNCTIONS
+/*++
+
+STDAPI
+StringCbLength(
+ IN LPCTSTR psz,
+ IN size_t cbMax,
+ OUT size_t* pcb OPTIONAL
+ );
+
+Routine Description:
+
+ This routine is a safer version of the C built-in function 'strlen'.
+ It is used to make sure a string is not larger than a given length, and
+ it optionally returns the current length in bytes not including
+ the null terminator.
+
+ This function returns a hresult, and not a pointer. It returns
+ S_OK if the string is non-null and the length including the null
+ terminator is less than or equal to cbMax bytes.
+
+Arguments:
+
+ psz - string to check the length of
+
+ cbMax - maximum number of bytes including the null terminator
+ that psz is allowed to contain
+
+ pcb - if the function succeeds and pcb is non-null, the current length
+ in bytes of psz excluding the null terminator will be returned.
+ This out parameter is equivalent to the return value of strlen(psz) * sizeof(TCHAR)
+
+Notes:
+ psz can be null but the function will fail
+
+ cbMax should be greater than or equal to sizeof(TCHAR) or the function will fail
+
+Return Value:
+
+ S_OK - psz is non-null and the length including the null
+ terminator is less than or equal to cbMax bytes
+
+ failure - you can use the macro HRESULT_CODE() to get a win32
+ error code for all hresult failure cases
+
+ It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
+ return value of this function.
+
+--*/
+
+STRSAFEAPI StringCbLengthA(const char* psz, size_t cchMax, size_t* pcch);
+
+#define StringCbLength StringCbLengthA
+
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCbLengthA(const char* psz, size_t cbMax, size_t* pcb)
+{
+ HRESULT hr;
+ size_t cchMax;
+ size_t cch = 0;
+
+ cchMax = cbMax / sizeof(char);
+
+ if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = StringLengthWorkerA(psz, cchMax, &cch);
+ }
+
+ if (SUCCEEDED(hr) && pcb)
+ {
+ // safe to multiply cch * sizeof(char) since cch < STRSAFE_MAX_CCH and sizeof(char) is 1
+ *pcb = cch * sizeof(char);
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+#endif // !STRSAFE_NO_CB_FUNCTIONS
+
+
+// these are the worker functions that actually do the work
+#ifdef STRSAFE_INLINE
+STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ while (cchDest && (*pszSrc != '\0'))
+ {
+ *pszDest++ = *pszSrc++;
+ cchDest--;
+ }
+
+ if (cchDest == 0)
+ {
+ // we are going to truncate pszDest
+ pszDest--;
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDest= '\0';
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually src data to copy
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while (cchRemaining && (*pszSrc != '\0'))
+ {
+ *pszDestEnd++= *pszSrc++;
+ cchRemaining--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+ else
+ {
+ // we are going to truncate pszDest
+ pszDestEnd--;
+ cchRemaining++;
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ while (cchDest && cchSrc && (*pszSrc != '\0'))
+ {
+ *pszDest++= *pszSrc++;
+ cchDest--;
+ cchSrc--;
+ }
+
+ if (cchDest == 0)
+ {
+ // we are going to truncate pszDest
+ pszDest--;
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDest= '\0';
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually src data to copy
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while (cchRemaining && cchSrc && (*pszSrc != '\0'))
+ {
+ *pszDestEnd++= *pszSrc++;
+ cchRemaining--;
+ cchSrc--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+ else
+ {
+ // we are going to truncate pszDest
+ pszDestEnd--;
+ cchRemaining++;
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc)
+{
+ HRESULT hr;
+ size_t cchDestCurrent;
+
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCopyWorkerA(pszDest + cchDestCurrent,
+ cchDest - cchDestCurrent,
+ pszSrc);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ size_t cchDestCurrent;
+
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest == 0) && (cbDest == 0))
+ {
+ cchDestCurrent = 0;
+ }
+ else
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ // only fail if there was actually src data to append
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
+ // those flags through
+ hr = StringCopyExWorkerA(pszDestEnd,
+ cchRemaining,
+ (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)),
+ pszSrc,
+ &pszDestEnd,
+ &cchRemaining,
+ dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ // STRSAFE_NO_TRUNCATION is taken care of by StringCopyExWorkerA()
+
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & STRSAFE_NULL_ON_FAILURE)
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend)
+{
+ HRESULT hr;
+ size_t cchDestCurrent;
+
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCopyNWorkerA(pszDest + cchDestCurrent,
+ cchDest - cchDestCurrent,
+ pszSrc,
+ cchMaxAppend);
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+ size_t cchDestCurrent = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest == 0) && (cbDest == 0))
+ {
+ cchDestCurrent = 0;
+ }
+ else
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (pszSrc == NULL)
+ {
+ pszSrc = "";
+ }
+ }
+ else
+ {
+ hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
+
+ if (SUCCEEDED(hr))
+ {
+ pszDestEnd = pszDest + cchDestCurrent;
+ cchRemaining = cchDest - cchDestCurrent;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ // only fail if there was actually src data to append
+ if (*pszSrc != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
+ // those flags through
+ hr = StringCopyNExWorkerA(pszDestEnd,
+ cchRemaining,
+ (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)),
+ pszSrc,
+ cchMaxAppend,
+ &pszDestEnd,
+ &cchRemaining,
+ dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ // STRSAFE_NO_TRUNCATION is taken care of by StringCopyNExWorkerA()
+
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList)
+{
+ HRESULT hr = S_OK;
+
+ if (cchDest == 0)
+ {
+ // can not null terminate a zero-byte dest buffer
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ int iRet;
+ size_t cchMax;
+
+ // leave the last space for the null terminator
+ cchMax = cchDest - 1;
+
+ iRet = vsnprintf(pszDest, cchMax, pszFormat, argList);
+ // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
+
+ if ((iRet < 0) || (((size_t)iRet) > cchMax))
+ {
+ // need to null terminate the string
+ pszDest += cchMax;
+ *pszDest = '\0';
+
+ // we have truncated pszDest
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else if (((size_t)iRet) == cchMax)
+ {
+ // need to null terminate the string
+ pszDest += cchMax;
+ *pszDest = '\0';
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+
+ if (pszFormat == NULL)
+ {
+ pszFormat = "";
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = 0;
+
+ // only fail if there was actually a non-empty format string
+ if (*pszFormat != '\0')
+ {
+ if (pszDest == NULL)
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ }
+ }
+ else
+ {
+ int iRet;
+ size_t cchMax;
+
+ // leave the last space for the null terminator
+ cchMax = cchDest - 1;
+
+ iRet = vsnprintf(pszDest, cchMax, pszFormat, argList);
+ // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
+
+ if ((iRet < 0) || (((size_t)iRet) > cchMax))
+ {
+ // we have truncated pszDest
+ pszDestEnd = pszDest + cchMax;
+ cchRemaining = 1;
+
+ // need to null terminate the string
+ *pszDestEnd = '\0';
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else if (((size_t)iRet) == cchMax)
+ {
+ // string fit perfectly
+ pszDestEnd = pszDest + cchMax;
+ cchRemaining = 1;
+
+ // need to null terminate the string
+ *pszDestEnd = '\0';
+ }
+ else if (((size_t)iRet) < cchMax)
+ {
+ // there is extra room
+ pszDestEnd = pszDest + iRet;
+ cchRemaining = cchDest - iRet;
+
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch)
+{
+ HRESULT hr = S_OK;
+ size_t cchMaxPrev = cchMax;
+
+ while (cchMax && (*psz != '\0'))
+ {
+ psz++;
+ cchMax--;
+ }
+
+ if (cchMax == 0)
+ {
+ // the string is longer than cchMax
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+
+ if (SUCCEEDED(hr) && pcch)
+ {
+ *pcch = cchMaxPrev - cchMax;
+ }
+
+ return hr;
+}
+
+#endif // STRSAFE_INLINE
+
+#ifndef STRSAFE_LIB_IMPL
+STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
+{
+ HRESULT hr = S_OK;
+ char* pszDestEnd = pszDest;
+ size_t cchRemaining = 0;
+
+ // ASSERT(cbDest == (cchDest * sizeof(char)) ||
+ // cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
+
+ // only accept valid flags
+ if (dwFlags & (~STRSAFE_VALID_FLAGS))
+ {
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ else
+ {
+ if (dwFlags & STRSAFE_IGNORE_NULLS)
+ {
+ if (pszDest == NULL)
+ {
+ if ((cchDest != 0) || (cbDest != 0))
+ {
+ // NULL pszDest and non-zero cchDest/cbDest is invalid
+ hr = STRSAFE_E_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (cchDest <= 1)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ if (cchDest == 1)
+ {
+ *pszDestEnd = '\0';
+ }
+
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+ }
+ else
+ {
+ char ch;
+
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ while ((cchRemaining > 1) && (ch = (char)getc(stdin)) != '\n')
+ {
+ if (ch == EOF)
+ {
+ if (pszDestEnd == pszDest)
+ {
+ // we failed to read anything from stdin
+ hr = STRSAFE_E_END_OF_FILE;
+ }
+ break;
+ }
+
+ *pszDestEnd = ch;
+
+ pszDestEnd++;
+ cchRemaining--;
+ }
+
+ if (cchRemaining > 0)
+ {
+ // there is extra room
+ if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
+ {
+ memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
+ }
+ }
+
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (pszDest)
+ {
+ if (dwFlags & STRSAFE_FILL_ON_FAILURE)
+ {
+ memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
+
+ if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+ }
+ else if (cchDest > 0)
+ {
+ pszDestEnd = pszDest + cchDest - 1;
+ cchRemaining = 1;
+
+ // null terminate the end of the string
+ *pszDestEnd = '\0';
+ }
+ }
+
+ if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
+ {
+ if (cchDest > 0)
+ {
+ pszDestEnd = pszDest;
+ cchRemaining = cchDest;
+
+ // null terminate the beginning of the string
+ *pszDestEnd = '\0';
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) ||
+ (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
+ (hr == STRSAFE_E_END_OF_FILE))
+ {
+ if (ppszDestEnd)
+ {
+ *ppszDestEnd = pszDestEnd;
+ }
+
+ if (pcchRemaining)
+ {
+ *pcchRemaining = cchRemaining;
+ }
+ }
+
+ return hr;
+}
+
+#endif // !STRSAFE_LIB_IMPL
+
+
+// Do not call these functions, they are worker functions for internal use within this file
+#ifdef DEPRECATE_SUPPORTED
+#pragma deprecated(StringCopyWorkerA)
+#pragma deprecated(StringCopyWorkerW)
+#pragma deprecated(StringCopyExWorkerA)
+#pragma deprecated(StringCopyExWorkerW)
+#pragma deprecated(StringCatWorkerA)
+#pragma deprecated(StringCatWorkerW)
+#pragma deprecated(StringCatExWorkerA)
+#pragma deprecated(StringCatExWorkerW)
+#pragma deprecated(StringCatNWorkerA)
+#pragma deprecated(StringCatNWorkerW)
+#pragma deprecated(StringCatNExWorkerA)
+#pragma deprecated(StringCatNExWorkerW)
+#pragma deprecated(StringVPrintfWorkerA)
+#pragma deprecated(StringVPrintfWorkerW)
+#pragma deprecated(StringVPrintfExWorkerA)
+#pragma deprecated(StringVPrintfExWorkerW)
+#pragma deprecated(StringLengthWorkerA)
+#pragma deprecated(StringLengthWorkerW)
+#else
+#define StringCopyWorkerA StringCopyWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
+#define StringCopyWorkerW StringCopyWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
+#define StringCopyExWorkerA StringCopyExWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
+#define StringCopyExWorkerW StringCopyExWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
+#define StringCatWorkerA StringCatWorkerA_instead_use_StringCchCatA_or_StringCchCatExA;
+#define StringCatWorkerW StringCatWorkerW_instead_use_StringCchCatW_or_StringCchCatExW;
+#define StringCatExWorkerA StringCatExWorkerA_instead_use_StringCchCatA_or_StringCchCatExA;
+#define StringCatExWorkerW StringCatExWorkerW_instead_use_StringCchCatW_or_StringCchCatExW;
+#define StringCatNWorkerA StringCatNWorkerA_instead_use_StringCchCatNA_or_StrincCbCatNA;
+#define StringCatNWorkerW StringCatNWorkerW_instead_use_StringCchCatNW_or_StringCbCatNW;
+#define StringCatNExWorkerA StringCatNExWorkerA_instead_use_StringCchCatNExA_or_StringCbCatNExA;
+#define StringCatNExWorkerW StringCatNExWorkerW_instead_use_StringCchCatNExW_or_StringCbCatNExW;
+#define StringVPrintfWorkerA StringVPrintfWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA;
+#define StringVPrintfWorkerW StringVPrintfWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW;
+#define StringVPrintfExWorkerA StringVPrintfExWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA;
+#define StringVPrintfExWorkerW StringVPrintfExWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW;
+#define StringLengthWorkerA StringLengthWorkerA_instead_use_StringCchLengthA_or_StringCbLengthA;
+#define StringLengthWorkerW StringLengthWorkerW_instead_use_StringCchLengthW_or_StringCbLengthW;
+#endif // !DEPRECATE_SUPPORTED
+
+#ifdef _NTSTRSAFE_H_INCLUDED_
+#pragma warning(pop)
+#endif // _NTSTRSAFE_H_INCLUDED_
+
+#endif
diff --git a/Src/replicant/nu/utf.c b/Src/replicant/nu/utf.c
new file mode 100644
index 000000000..04495aa59
--- /dev/null
+++ b/Src/replicant/nu/utf.c
@@ -0,0 +1,649 @@
+#include "utf.h"
+
+#include "ByteReader.h"
+#include "ByteWriter.h"
+#include "foundation/error.h"
+#include <string.h>
+
+static const uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE};
+
+static const uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC};
+
+// returns the number of utf-16 words required to store a given codepoint
+static size_t ucs4_to_utf16_count(uint32_t codepoint)
+{
+ if (codepoint >= 0x110000)
+ return 0; // out of bounds
+
+ if (codepoint >= 0x10000)
+ return 2;
+
+ return 1;
+}
+
+static int utf16LE_to_ucs4_character(bytereader_t const byte_reader, uint32_t *codepoint)
+{
+ uint16_t lead;
+
+ lead = bytereader_read_u16_le(byte_reader);
+ if (lead < 0xD800 || lead >= 0xE000)
+ {
+ *codepoint = lead;
+ return NErr_Success;
+ }
+
+ if (lead < 0xDC00)
+ {
+ if (bytereader_size(byte_reader) >= 2)
+ {
+ uint16_t trail = bytereader_read_u16_le(byte_reader);
+ if (trail >= 0xDC00 && trail < 0xE000)
+ {
+ *codepoint = 0x10000 + ((lead - 0xD800) << 10) + (trail - 0xDC00);
+ return NErr_Success;
+ }
+ }
+ }
+
+ return NErr_Error; // invalid
+}
+
+static int utf16BE_to_ucs4_character(bytereader_t const byte_reader, uint32_t *codepoint)
+{
+ uint16_t lead;
+
+ lead = bytereader_read_u16_be(byte_reader);
+ if (lead < 0xD800 || lead >= 0xE000)
+ {
+ *codepoint = lead;
+ return NErr_Success;
+ }
+
+ if (lead < 0xDC00)
+ {
+ if (bytereader_size(byte_reader) >= 2)
+ {
+ uint16_t trail = bytereader_read_u16_be(byte_reader);
+ if (trail >= 0xDC00 && trail < 0xE000)
+ {
+ *codepoint = 0x10000 + ((lead - 0xD800) << 10) + (trail - 0xDC00);
+ return NErr_Success;
+ }
+ }
+ }
+
+ return NErr_Error; // invalid
+}
+
+static size_t utf8_to_ucs4_character(const char *utf8, size_t len, uint32_t *codepoint)
+{
+ uint32_t res=0;
+ size_t n;
+ size_t cnt=0;
+ while(1)
+ {
+ if ((*utf8&mask_tab[cnt])==val_tab[cnt]) break;
+ if (++cnt==6) return 0;
+ }
+ cnt++;
+
+
+ if (cnt==2 && !(*utf8&0x1E))
+ return 0;
+
+ if (cnt==1)
+ res=*utf8;
+ else
+ res=(0xFF>>(cnt+1))&*utf8;
+
+ if (cnt > len)
+ return 0;
+
+ for (n=1;n<cnt;n++)
+ {
+ if ((utf8[n]&0xC0) != 0x80)
+ return 0;
+ if (!res && n==2 && !((utf8[n]&0x7F) >> (7 - cnt)))
+ return 0;
+
+ res=(res<<6)|(utf8[n]&0x3F);
+ }
+
+ if (codepoint)
+ *codepoint=res;
+
+ return cnt;
+}
+
+// returns the number of utf-8 bytes required to store a given codepoint
+static size_t ucs4_to_utf8_count(uint32_t codepoint)
+{
+ if (codepoint < 0x80)
+ return 1;
+ else if (codepoint < 0x800)
+ return 2;
+ else if (codepoint < 0x10000)
+ return 3;
+ else if (codepoint < 0x200000)
+ return 4;
+ else if (codepoint < 0x4000000)
+ return 5;
+ else if (codepoint <= 0x7FFFFFFF)
+ return 6;
+ else
+ return 0;
+}
+
+static size_t ucs4_to_utf8_character(char *target, uint32_t codepoint, size_t max)
+{
+ size_t count = ucs4_to_utf8_count(codepoint);
+
+ if (!count)
+ return 0;
+
+ if (count>max) return 0;
+
+ if (target == 0)
+ return count;
+
+ switch (count)
+ {
+ case 6:
+ target[5] = 0x80 | (codepoint & 0x3F);
+ codepoint = codepoint >> 6;
+ codepoint |= 0x4000000;
+ case 5:
+ target[4] = 0x80 | (codepoint & 0x3F);
+ codepoint = codepoint >> 6;
+ codepoint |= 0x200000;
+ case 4:
+ target[3] = 0x80 | (codepoint & 0x3F);
+ codepoint = codepoint >> 6;
+ codepoint |= 0x10000;
+ case 3:
+ target[2] = 0x80 | (codepoint & 0x3F);
+ codepoint = codepoint >> 6;
+ codepoint |= 0x800;
+ case 2:
+ target[1] = 0x80 | (codepoint & 0x3F);
+ codepoint = codepoint >> 6;
+ codepoint |= 0xC0;
+ case 1:
+ target[0] = codepoint;
+ }
+
+ return count;
+}
+
+static size_t ucs4_to_utf16LE_character(bytewriter_t byte_writer, uint32_t codepoint)
+{
+ if (codepoint >= 0x110000)
+ return 0;
+
+ if (codepoint >= 0x10000)
+ {
+ if (bytewriter_size(byte_writer) < 4)
+ return 0;
+
+ bytewriter_write_u16_le(byte_writer, ((codepoint - 0x10000) >> 10) + 0xD800); // high surrogate
+ bytewriter_write_u16_le(byte_writer, ((codepoint - 0x10000) & 0x3FF) + 0xDC00); // low surrogate
+ return 2;
+ }
+ else
+ {
+ bytewriter_write_u16_le(byte_writer, codepoint);
+ return 1;
+ }
+}
+
+static size_t ucs4_to_utf16BE_character(bytewriter_t byte_writer, uint32_t codepoint)
+{
+ if (codepoint >= 0x110000)
+ return 0;
+
+ if (codepoint >= 0x10000)
+ {
+ if (bytewriter_size(byte_writer) < 4)
+ return 0;
+
+ bytewriter_write_u16_be(byte_writer, ((codepoint - 0x10000) >> 10) + 0xD800); // high surrogate
+ bytewriter_write_u16_be(byte_writer, ((codepoint - 0x10000) & 0x3FF) + 0xDC00); // low surrogate
+ return 2;
+ }
+ else
+ {
+ bytewriter_write_u16_be(byte_writer, codepoint);
+ return 1;
+ }
+}
+
+size_t utf16LE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+ bytereader_s byte_reader;
+ bytereader_init(&byte_reader, src, source_len*2);
+
+ if (!dst) // they just want the size
+ {
+ while (bytereader_size(&byte_reader))
+ {
+ if (utf16LE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
+ break;
+
+ characters_processed = ucs4_to_utf8_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+ while(bytereader_size(&byte_reader) && position<out_len)
+ {
+ if (utf16LE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
+ break;
+
+ characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
+ if (!characters_processed)
+ break;
+ position+=characters_processed;
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+size_t utf16BE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+ bytereader_s byte_reader;
+ bytereader_init(&byte_reader, src, source_len*2);
+
+ if (!dst) // they just want the size
+ {
+ while (bytereader_size(&byte_reader))
+ {
+ if (utf16BE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
+ break;
+
+ characters_processed = ucs4_to_utf8_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+ while(bytereader_size(&byte_reader) && position<out_len)
+ {
+ if (utf16BE_to_ucs4_character(&byte_reader, &codepoint) != NErr_Success)
+ break;
+
+ characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
+ if (!characters_processed)
+ break;
+ position+=characters_processed;
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+
+size_t ucs4_to_utf8(const uint32_t *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+ bytereader_s byte_reader;
+ bytereader_init(&byte_reader, src, source_len*4);
+
+ if (!dst) // they just want the size
+ {
+ while (bytereader_size(&byte_reader) > 3)
+ {
+ codepoint = bytereader_read_u32_le(&byte_reader);
+
+ characters_processed = ucs4_to_utf8_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+ while(bytereader_size(&byte_reader) > 3 && position<out_len)
+ {
+ codepoint = bytereader_read_u32_le(&byte_reader);
+
+ characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
+ if (!characters_processed)
+ break;
+ position+=characters_processed;
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+size_t utf8_to_utf16LE(const char *src, size_t source_len, uint16_t *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t characters_processed;
+ bytewriter_s byte_writer;
+
+ if (!dst) // they just want the size
+ {
+ size_t position=0;
+ while (source_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ characters_processed = ucs4_to_utf16_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+
+ bytewriter_init(&byte_writer, dst, out_len*2);
+ while(source_len && bytewriter_size(&byte_writer))
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ characters_processed=ucs4_to_utf16LE_character(&byte_writer, codepoint);
+ if (!characters_processed)
+ break;
+ }
+ if (bytewriter_size(&byte_writer))
+ bytewriter_write_u16_le(&byte_writer, 0);
+ return out_len - bytewriter_size(&byte_writer)/2;
+}
+
+size_t utf8_to_utf16BE(const char *src, size_t source_len, uint16_t *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t characters_processed;
+ bytewriter_s byte_writer;
+
+ if (!dst) // they just want the size
+ {
+ size_t position=0;
+ while (source_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ characters_processed = ucs4_to_utf16_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+ bytewriter_init(&byte_writer, dst, out_len*2);
+ while(source_len && bytewriter_size(&byte_writer))
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ characters_processed=ucs4_to_utf16BE_character(&byte_writer, codepoint);
+ if (!characters_processed)
+ break;
+
+ }
+ if (bytewriter_size(&byte_writer))
+ bytewriter_write_u16_be(&byte_writer, 0);
+
+ return out_len - bytewriter_size(&byte_writer)/2;
+
+}
+
+size_t utf8_to_ISO_8859_1(const char *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+
+ if (!dst) // they just want the size
+ {
+ while (source_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+ position++;
+ }
+ return position;
+ }
+
+ while(source_len && position<out_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ if (codepoint < 256)
+ dst[position++] = codepoint;
+ else
+ dst[position++] = '?';
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+size_t ISO_8859_1_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+
+ if (!dst) // they just want the size
+ {
+ while (source_len)
+ {
+ codepoint = *src++;
+ source_len--;
+
+ characters_processed = ucs4_to_utf8_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+ while(source_len && position<out_len)
+ {
+ codepoint = *src++;
+
+ source_len--;
+
+ characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+size_t utf8_to_ucs4(const char *src, size_t source_len, uint32_t *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t characters_processed;
+ bytewriter_s byte_writer;
+
+ if (!dst) // they just want the size
+ {
+ size_t position=0;
+ while (source_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ characters_processed = 1;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+ bytewriter_init(&byte_writer, dst, out_len*4);
+ while(source_len && bytewriter_size(&byte_writer))
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ bytewriter_write_u32_le(&byte_writer, codepoint);
+ }
+ if (bytewriter_size(&byte_writer))
+ bytewriter_write_u32_le(&byte_writer, 0);
+ return out_len - bytewriter_size(&byte_writer)/4;
+}
+
+size_t ASCII_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+
+ if (!dst) // they just want the size
+ {
+ while (source_len)
+ {
+ codepoint = *src++;
+ source_len--;
+
+ characters_processed = ucs4_to_utf8_count(codepoint);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ return position;
+ }
+
+ while(source_len && position<out_len)
+ {
+ codepoint = *src++;
+
+ source_len--;
+
+ characters_processed=ucs4_to_utf8_character(&dst[position], codepoint, out_len-position);
+ if (!characters_processed)
+ break;
+
+ position+=characters_processed;
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+size_t utf8_to_ASCII(const char *src, size_t source_len, char *dst, size_t out_len)
+{
+ uint32_t codepoint;
+ size_t position=0;
+ size_t characters_processed;
+
+ if (!dst) // they just want the size
+ {
+ while (source_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+ position++;
+ }
+ return position;
+ }
+
+ while(source_len && position<out_len)
+ {
+ characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+
+ if (codepoint < 128)
+ dst[position++] = codepoint;
+ else
+ dst[position++] = '?';
+ }
+ if (position<out_len)
+ dst[position]=0;
+ return position;
+}
+
+size_t utf8_strnlen(const char *src, size_t source_len, size_t codepoints)
+{
+ uint32_t codepoint = 0;
+ size_t position=0;
+ size_t i=0;
+
+ for (i=0;i<codepoints && *src;i++)
+ {
+ size_t characters_processed = utf8_to_ucs4_character(src, source_len, &codepoint);
+ if (codepoint == 0xFFFD)
+ break;
+
+ source_len -= characters_processed;
+ src += characters_processed;
+ position+=characters_processed;
+ }
+ return position;
+
+}
diff --git a/Src/replicant/nu/utf.h b/Src/replicant/nu/utf.h
new file mode 100644
index 000000000..cc1437e1f
--- /dev/null
+++ b/Src/replicant/nu/utf.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "foundation/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* to utf8 */
+ size_t utf16LE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len);
+ size_t utf16BE_to_utf8(const uint16_t *src, size_t source_len, char *dst, size_t out_len);
+ size_t ISO_8859_1_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len);
+ size_t ASCII_to_utf8(const char *src, size_t source_len, char *dst, size_t out_len);
+ size_t ucs4_to_utf8(const uint32_t *src, size_t source_len, char *dst, size_t out_len);
+
+ /* from utf8 */
+ size_t utf8_to_utf16LE(const char *src, size_t source_len, uint16_t *dst, size_t out_len);
+ size_t utf8_to_ISO_8859_1(const char *src, size_t source_len, char *dst, size_t out_len);
+ size_t utf8_to_utf16BE(const char *src, size_t source_len, uint16_t *dst, size_t out_len);
+ size_t utf8_to_ASCII(const char *src, size_t source_len, char *dst, size_t out_len);
+ size_t utf8_to_ucs4(const char *src, size_t source_len, uint32_t *dst, size_t out_len);
+
+ /* returns the number of bytes required to make the specified number of codepoints exactly fit */
+ size_t utf8_strnlen(const char *src, size_t source_len, size_t codepoints);
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/win-amd64/LockFreeLIFO.h b/Src/replicant/nu/win-amd64/LockFreeLIFO.h
new file mode 100644
index 000000000..5cf087e7f
--- /dev/null
+++ b/Src/replicant/nu/win-amd64/LockFreeLIFO.h
@@ -0,0 +1,34 @@
+#pragma once
+
+
+#include "nu/queue_node.h"
+#include <windows.h>
+#include <malloc.h>
+
+/* lock free stack object
+multiple threads can push and pop without locking
+note that order is not guaranteed. that is, if Thread 1 calls Insert before Thread 2, Thread 2's item might still make it in before.
+*/
+
+#ifdef __cplusplus
+#define NU_LIFO_INLINE inline
+extern "C" {
+#else
+#define NX_ATOMIC_INLINE
+#endif
+
+ typedef SLIST_HEADER lifo_t;
+
+ /* use this to allocate an object that will go into this */
+ NU_LIFO_INLINE static queue_node_t *lifo_malloc(size_t bytes) { return (queue_node_t *)_aligned_malloc(bytes, MEMORY_ALLOCATION_ALIGNMENT); }
+ NU_LIFO_INLINE static void lifo_free(queue_node_t *ptr) { _aligned_free(ptr); }
+
+ NU_LIFO_INLINE static lifo_t *lifo_create() { return (lifo_t *)_aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT); }
+ NU_LIFO_INLINE static void lifo_destroy(lifo_t *lifo) { _aligned_free(lifo); }
+ NU_LIFO_INLINE static void lifo_init(lifo_t *lifo) { InitializeSListHead(lifo); }
+ NU_LIFO_INLINE static void lifo_push(lifo_t *lifo, queue_node_t *cl) { InterlockedPushEntrySList(lifo, cl); }
+ NU_LIFO_INLINE static queue_node_t *lifo_pop(lifo_t *lifo) { return InterlockedPopEntrySList(lifo); }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/win-amd64/ThreadLoop.cpp b/Src/replicant/nu/win-amd64/ThreadLoop.cpp
new file mode 100644
index 000000000..4c78a4316
--- /dev/null
+++ b/Src/replicant/nu/win-amd64/ThreadLoop.cpp
@@ -0,0 +1,70 @@
+#include "ThreadLoop.h"
+
+lifo_t ThreadLoop::procedure_cache = {0,};
+lifo_t ThreadLoop::cache_bases= {0,};
+
+#define PROCEDURE_CACHE_SEED 64
+ThreadLoop::ThreadLoop()
+{
+ mpscq_init(&procedure_queue);
+ procedure_notification = CreateSemaphore(0, 0, LONG_MAX, 0);
+ kill_switch = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+void ThreadLoop::RefillCache()
+{
+ threadloop_node_t *cache_seed = (threadloop_node_t *)lifo_malloc(PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
+ if (cache_seed)
+ {
+ memset(cache_seed, 0, PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
+ for (int i=0;i<PROCEDURE_CACHE_SEED;i++)
+ {
+ lifo_push(&procedure_cache, &cache_seed[i]);
+ }
+ lifo_push(&cache_bases, cache_seed);
+ }
+}
+
+void ThreadLoop::Run()
+{
+ HANDLE events[] = {kill_switch, procedure_notification};
+ while (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
+ {
+ threadloop_node_t *apc;
+ for (;;)
+ {
+ apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
+ if (!apc)
+ {
+ Sleep(0);
+ continue;
+ }
+ apc->func(apc->param1, apc->param2, apc->real_value);
+ }
+ lifo_push(&procedure_cache, apc);
+ }
+}
+
+threadloop_node_t *ThreadLoop::GetAPC()
+{
+ threadloop_node_t *apc = 0;
+
+ do
+ {
+ apc = (threadloop_node_t *)lifo_pop(&procedure_cache);
+ if (!apc)
+ RefillCache();
+ } while (!apc);
+ return apc;
+}
+
+void ThreadLoop::Schedule(threadloop_node_t *apc)
+{
+ mpscq_push(&procedure_queue, apc);
+ ReleaseSemaphore(procedure_notification, 1, 0);
+}
+
+void ThreadLoop::Kill()
+{
+ SetEvent(kill_switch);
+}
diff --git a/Src/replicant/nu/win-amd64/ThreadLoop.h b/Src/replicant/nu/win-amd64/ThreadLoop.h
new file mode 100644
index 000000000..f1552b51e
--- /dev/null
+++ b/Src/replicant/nu/win-amd64/ThreadLoop.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "nu/lfmpscq.h"
+#include "nu/LockFreeLIFO.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+struct threadloop_node_t : public queue_node_t
+{
+ void (*func)(void *param1, void *param2, double real_value);
+
+ void *param1;
+ void *param2;
+ double real_value;
+};
+
+class ThreadLoop
+{
+public:
+ ThreadLoop();
+ threadloop_node_t *GetAPC(); // returns a node for you to fill out
+ void Schedule(threadloop_node_t *apc);
+ void Run();
+ void Kill();
+private:
+ void RefillCache();
+
+ HANDLE procedure_notification;
+ HANDLE kill_switch;
+ mpscq_t procedure_queue;
+
+ /* Memory cache to be able to run APCs without having the memory manager lock
+ we'll allocate 100 at a time (#defined by PROCEDURE_CACHE_SEED)
+ and allocate new ones only if the cache is empty (which unfortunately will lock)
+ cache_bases holds the pointers we've allocated (to free on destruction of this object)
+ and procedure_cache holds the individual pointers */
+ static lifo_t procedure_cache;
+ static lifo_t cache_bases;
+}; \ No newline at end of file
diff --git a/Src/replicant/nu/win-x86/LockFreeLIFO.asm b/Src/replicant/nu/win-x86/LockFreeLIFO.asm
new file mode 100644
index 000000000..dd67e5b15
--- /dev/null
+++ b/Src/replicant/nu/win-x86/LockFreeLIFO.asm
@@ -0,0 +1,50 @@
+.686
+.model FLAT
+
+PUBLIC _lifo_push
+_TEXT SEGMENT
+lifo = 4 ; size = 4
+entry = 8 ; size = 4
+_lifo_push PROC
+ mov ecx, DWORD PTR 4[esp] ; ecx holds lifo
+ mov edx, DWORD PTR 8[esp] ; edx holds the new entry
+again:
+ mov eax, DWORD PTR [ecx] ; eax holds the old head
+ mov DWORD PTR[edx], eax ; new node's 'next' is set to the old head
+ lock cmpxchg DWORD PTR [ecx], edx
+ jnz again
+ ret 0
+_lifo_push ENDP
+
+PUBLIC _lifo_pop
+_TEXT SEGMENT
+lifo = 4 ; size = 4
+_lifo_pop PROC
+ push esi
+ push ebx
+ mov esi, DWORD PTR 12[esp] ; esi holds lifo
+again:
+ ; if re-ordered loads become an issue, we could use cmpxchg8b to read in (after zeroing ebx/ecx) or maybe use movq
+ mov edx, DWORD PTR [esi+4] ; counter
+ ; or we could put an LFENCE here
+ mov eax, DWORD PTR [esi] ; pointer
+ test eax, eax
+ jz bail
+
+ mov ecx, edx ; counter
+ mov ebx, DWORD PTR [eax] ; pointer->next
+ inc ecx
+
+ lock cmpxchg8b QWORD PTR [esi]
+ jnz again
+
+bail:
+ pop ebx
+ pop esi
+ ret 0
+_lifo_pop ENDP
+
+_TEXT ENDS
+
+END
+
diff --git a/Src/replicant/nu/win-x86/LockFreeLIFO.c b/Src/replicant/nu/win-x86/LockFreeLIFO.c
new file mode 100644
index 000000000..ecffde9a7
--- /dev/null
+++ b/Src/replicant/nu/win-x86/LockFreeLIFO.c
@@ -0,0 +1,48 @@
+#include "LockFreeLIFO.h"
+#include "foundation/atomics.h"
+
+/* win32 implementation */
+
+void lifo_init(lifo_t *lifo)
+{
+ lifo->head = 0;
+ lifo->aba = 0;
+}
+
+#if 0 // defined in LockFreeLIFO.asm
+void lifo_push(lifo_t *lifo, queue_node_t *cl)
+{
+ queue_node_t *new_head = cl;
+ queue_node_t *old_head = 0;
+ do
+ {
+ old_head = (queue_node_t *)lifo->head;
+ new_head->Next = old_head;
+ } while (!nx_atomic_cmpxchg_pointer(old_head, new_head, (void * volatile *)&lifo->head));
+}
+
+queue_node_t *lifo_pop(lifo_t *lifo)
+{
+ lifo_t old_head, new_head;
+ do
+ {
+ old_head = *lifo;
+ if (!old_head.head)
+ return 0;
+
+ new_head.head = old_head.head->Next;
+ new_head.aba = old_head.aba+1;
+ } while (!nx_atomic_cmpxchg2(*(int64_t *)&old_head, *(int64_t *)&new_head, (volatile int64_t *)&lifo->head));
+ return (queue_node_t *)old_head.head;
+}
+#endif
+
+queue_node_t *lifo_malloc(size_t bytes)
+{
+ return _aligned_malloc(bytes, MEMORY_ALLOCATION_ALIGNMENT);
+}
+
+void lifo_free(queue_node_t *ptr)
+{
+ _aligned_free(ptr);
+} \ No newline at end of file
diff --git a/Src/replicant/nu/win-x86/LockFreeLIFO.h b/Src/replicant/nu/win-x86/LockFreeLIFO.h
new file mode 100644
index 000000000..90ba9d335
--- /dev/null
+++ b/Src/replicant/nu/win-x86/LockFreeLIFO.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "foundation/types.h"
+#include "nu/queue_node.h"
+#include "foundation/align.h"
+/* lock free stack object
+multiple threads can push and pop without locking
+note that order is not guaranteed. that is, if Thread 1 calls Insert before Thread 2, Thread 2's item might still make it in before.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef NALIGN(8) struct lifo_struct_t
+ {
+ volatile queue_node_t *head;
+ uint32_t aba;
+ } lifo_t;
+
+ /* use this to allocate an object that will go into this */
+ queue_node_t *lifo_malloc(size_t bytes);
+ void lifo_free(queue_node_t *ptr);
+
+ void lifo_init(lifo_t *lifo);
+ void lifo_push(lifo_t *lifo, queue_node_t *cl);
+ queue_node_t *lifo_pop(lifo_t *lifo);
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nu/win-x86/ThreadLoop.cpp b/Src/replicant/nu/win-x86/ThreadLoop.cpp
new file mode 100644
index 000000000..5dae339d6
--- /dev/null
+++ b/Src/replicant/nu/win-x86/ThreadLoop.cpp
@@ -0,0 +1,84 @@
+#include "ThreadLoop.h"
+
+lifo_t ThreadLoop::procedure_cache = {0,};
+lifo_t ThreadLoop::cache_bases= {0,};
+
+#define PROCEDURE_CACHE_SEED 64
+ThreadLoop::ThreadLoop()
+{
+ mpscq_init(&procedure_queue);
+ procedure_notification = CreateSemaphore(0, 0, LONG_MAX, 0);
+ kill_switch = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+void ThreadLoop::RefillCache()
+{
+ threadloop_node_t *cache_seed = (threadloop_node_t *)malloc(PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
+
+ if (cache_seed)
+ {
+ int i=PROCEDURE_CACHE_SEED;
+ while (--i)
+ {
+ lifo_push(&procedure_cache, (queue_node_t *)&cache_seed[i]);
+ }
+ lifo_push(&cache_bases, (queue_node_t *)cache_seed);
+ }
+ else
+ {
+ Sleep(0); // yield and hope that someone else pops something off soon
+ }
+
+}
+
+void ThreadLoop::Run()
+{
+ HANDLE events[] = {kill_switch, procedure_notification};
+ while (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
+ {
+ for (;;)
+ {
+ threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
+ if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
+ {
+ Sleep(0); // yield so that the thread that got pre-empted during push can finish
+ }
+ else
+ {
+ if (apc)
+ {
+ apc->func(apc->param1, apc->param2, apc->real_value);
+ lifo_push(&procedure_cache, apc);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+threadloop_node_t *ThreadLoop::GetAPC()
+{
+ threadloop_node_t *apc = 0;
+
+ do
+ {
+ apc = (threadloop_node_t *)lifo_pop(&procedure_cache);
+ if (!apc)
+ RefillCache();
+ } while (!apc);
+ return apc;
+}
+
+void ThreadLoop::Schedule(threadloop_node_t *apc)
+{
+ if (mpscq_push(&procedure_queue, apc) == 0)
+ ReleaseSemaphore(procedure_notification, 1, 0);
+}
+
+void ThreadLoop::Kill()
+{
+ SetEvent(kill_switch);
+}
diff --git a/Src/replicant/nu/win-x86/ThreadLoop.h b/Src/replicant/nu/win-x86/ThreadLoop.h
new file mode 100644
index 000000000..cc412386f
--- /dev/null
+++ b/Src/replicant/nu/win-x86/ThreadLoop.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "nu/lfmpscq.h"
+#include "nu/LockFreeLIFO.h"
+#include <windows.h>
+
+struct threadloop_node_t : public queue_node_t
+{
+ void (*func)(void *param1, void *param2, double real_value);
+
+ void *param1;
+ void *param2;
+ double real_value;
+};
+
+class ThreadLoop
+{
+public:
+ ThreadLoop();
+ threadloop_node_t *GetAPC(); // returns a node for you to fill out
+ void Schedule(threadloop_node_t *apc);
+ void Run();
+ void Kill();
+private:
+ void RefillCache();
+
+ HANDLE procedure_notification;
+ HANDLE kill_switch;
+ mpscq_t procedure_queue;
+
+ /* Memory cache to be able to run APCs without having the memory manager lock
+ we'll allocate 100 at a time (#defined by PROCEDURE_CACHE_SEED)
+ and allocate new ones only if the cache is empty (which unfortunately will lock)
+ cache_bases holds the pointers we've allocated (to free on destruction of this object)
+ and procedure_cache holds the individual pointers */
+ static lifo_t procedure_cache;
+ static lifo_t cache_bases;
+}; \ No newline at end of file
diff --git a/Src/replicant/nu/win/MessageLoop.cpp b/Src/replicant/nu/win/MessageLoop.cpp
new file mode 100644
index 000000000..2353801c3
--- /dev/null
+++ b/Src/replicant/nu/win/MessageLoop.cpp
@@ -0,0 +1,121 @@
+#include "MessageLoop.h"
+#include <assert.h>
+
+lifo_t nu::MessageLoop::message_cache = {0,};
+lifo_t nu::MessageLoop::cache_bases= {0,};
+
+#define MESSAAGE_CACHE_SEED 64
+
+typedef uint8_t message_data_t[64]; // ensure all messages are this size
+
+nu::MessageLoop::MessageLoop()
+{
+ mpscq_init(&message_queue);
+ message_notification = CreateEvent(0, FALSE, FALSE, 0);
+}
+
+nu::MessageLoop::~MessageLoop()
+{
+ CloseHandle(message_notification);
+}
+
+void nu::MessageLoop::RefillCache()
+{
+ message_data_t *cache_seed = (message_data_t *)_aligned_malloc(MESSAAGE_CACHE_SEED*sizeof(message_data_t), 64);
+
+ if (cache_seed)
+ {
+ int i=MESSAAGE_CACHE_SEED;
+ while (--i)
+ {
+ lifo_push(&message_cache, (queue_node_t *)&cache_seed[i]);
+ }
+ lifo_push(&cache_bases, (queue_node_t *)cache_seed);
+ }
+ else
+ {
+ Sleep(0); // yield and hope that someone else pops something off soon
+ }
+}
+
+nu::message_node_t *nu::MessageLoop::AllocateMessage()
+{
+ message_node_t *apc = 0;
+
+ do
+ {
+ apc = (message_node_t *)lifo_pop(&message_cache);
+ if (!apc)
+ RefillCache();
+ } while (!apc);
+ return apc;
+}
+
+void nu::MessageLoop::PostMessage(nu::message_node_t *message)
+{
+ if (mpscq_push(&message_queue, message) == 0)
+ SetEvent(message_notification);
+}
+
+void nu::MessageLoop::FreeMessage(nu::message_node_t *message)
+{
+ lifo_push(&message_cache, message);
+}
+
+nu::message_node_t *nu::MessageLoop::GetMessage()
+{
+ message_node_t *message = PeekMessage();
+ if (message)
+ {
+ return message;
+ }
+
+ while (WaitForSingleObject(message_notification, INFINITE) == WAIT_OBJECT_0)
+ {
+ message = PeekMessage();
+ if (message)
+ {
+ return message;
+ }
+ }
+ return 0;
+}
+
+nu::message_node_t *nu::MessageLoop::PeekMessage()
+{
+ for (;;) // loop because we need to handle 'busy' from the queue
+ {
+ message_node_t *message = (message_node_t *)mpscq_pop(&message_queue);
+ if (message == (message_node_t *)1) /* special return value that indicates a busy list */
+ {
+ // benski> although it's tempting to return 0 here, doing so will mess up the Event logic
+ Sleep(0); // yield so that the thread that got pre-empted during push can finish
+ }
+ else
+ {
+ if (message)
+ {
+ return message;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+}
+
+nu::message_node_t *nu::MessageLoop::PeekMessage(unsigned int milliseconds)
+{
+ message_node_t *message = PeekMessage();
+ if (message)
+ return message;
+
+ if (WaitForSingleObject(message_notification, milliseconds) == WAIT_OBJECT_0)
+ {
+ message = PeekMessage();
+ if (message)
+ return message;
+ }
+ return 0;
+}
diff --git a/Src/replicant/nu/win/MessageLoop.h b/Src/replicant/nu/win/MessageLoop.h
new file mode 100644
index 000000000..88e4520f3
--- /dev/null
+++ b/Src/replicant/nu/win/MessageLoop.h
@@ -0,0 +1,45 @@
+#pragma once
+#include "foundation/types.h"
+#include "nu/lfmpscq.h"
+#include "nu/LockFreeLIFO.h"
+#include <windows.h>
+
+namespace nu
+{
+
+ /* you can inherit from message_node_t (or combine inside a struct)
+ but make sure that your message isn't > 64 bytes */
+ struct message_node_t : public queue_node_t
+ {
+ uint32_t message;
+ };
+
+ class MessageLoop
+ {
+ public:
+ MessageLoop();
+ ~MessageLoop();
+ /* API for Message senders */
+ message_node_t *AllocateMessage(); // returns a message for you to fill out
+ void PostMessage(message_node_t *message);
+
+ /* API for Message receivers */
+ void FreeMessage(message_node_t *message);
+ message_node_t *GetMessage(); // waits forever
+ message_node_t *PeekMessage();
+ message_node_t *PeekMessage(unsigned int milliseconds);
+ private:
+ void RefillCache();
+
+ HANDLE message_notification;
+ mpscq_t message_queue;
+
+ /* Memory cache to be able to run APCs without having the memory manager lock
+ we'll allocate 100 at a time (#defined by MESSAGE_CACHE_SEED)
+ and allocate new ones only if the cache is empty (which unfortunately will lock)
+ cache_bases holds the pointers we've allocated (to free on destruction of this object)
+ and message_cache holds the individual pointers */
+ static lifo_t message_cache;
+ static lifo_t cache_bases;
+ };
+} \ No newline at end of file
diff --git a/Src/replicant/nu/win/ThreadLoop.cpp b/Src/replicant/nu/win/ThreadLoop.cpp
new file mode 100644
index 000000000..3ff89e590
--- /dev/null
+++ b/Src/replicant/nu/win/ThreadLoop.cpp
@@ -0,0 +1,146 @@
+#include "ThreadLoop.h"
+#include <limits.h>
+
+lifo_t ThreadLoop::procedure_cache = {0,};
+lifo_t ThreadLoop::cache_bases= {0,};
+
+#define PROCEDURE_CACHE_SEED 64
+ThreadLoop::ThreadLoop()
+{
+ mpscq_init(&procedure_queue);
+ procedure_notification = CreateSemaphoreW(0, 0, LONG_MAX, 0);
+ kill_switch = CreateEvent(0, TRUE, FALSE, 0);
+}
+
+ThreadLoop::~ThreadLoop()
+{
+ CloseHandle(procedure_notification);
+ CloseHandle(kill_switch);
+}
+
+void ThreadLoop::RefillCache()
+{
+ threadloop_node_t *cache_seed = (threadloop_node_t *)malloc(PROCEDURE_CACHE_SEED*sizeof(threadloop_node_t));
+
+ if (cache_seed)
+ {
+ int i=PROCEDURE_CACHE_SEED;
+ while (--i)
+ {
+ lifo_push(&procedure_cache, (queue_node_t *)&cache_seed[i]);
+ }
+ lifo_push(&cache_bases, (queue_node_t *)cache_seed);
+ }
+ else
+ {
+ Sleep(0); // yield and hope that someone else pops something off soon
+ }
+}
+
+void ThreadLoop::Run()
+{
+ HANDLE events[] = {kill_switch, procedure_notification};
+ while (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
+ {
+ for (;;)
+ {
+ threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
+ if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
+ {
+ Sleep(0); // yield so that the thread that got pre-empted during push can finish
+ }
+ else
+ {
+ if (apc)
+ {
+ apc->func(apc->param1, apc->param2, apc->real_value);
+ lifo_push(&procedure_cache, apc);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void ThreadLoop::Step(unsigned int milliseconds)
+{
+ HANDLE events[] = {kill_switch, procedure_notification};
+ if (WaitForMultipleObjects(2, events, FALSE, milliseconds) == WAIT_OBJECT_0 + 1)
+ {
+ for (;;)
+ {
+ threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
+ if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
+ {
+ Sleep(0); // yield so that the thread that got pre-empted during push can finish
+ }
+ else
+ {
+ if (apc)
+ {
+ apc->func(apc->param1, apc->param2, apc->real_value);
+ lifo_push(&procedure_cache, apc);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void ThreadLoop::Step()
+{
+ HANDLE events[] = {kill_switch, procedure_notification};
+ if (WaitForMultipleObjects(2, events, FALSE, INFINITE) == WAIT_OBJECT_0 + 1)
+ {
+ for (;;)
+ {
+ threadloop_node_t *apc = (threadloop_node_t *)mpscq_pop(&procedure_queue);
+ if (apc == (threadloop_node_t *)1) /* special return value that indicates a busy list */
+ {
+ Sleep(0); // yield so that the thread that got pre-empted during push can finish
+ }
+ else
+ {
+ if (apc)
+ {
+ apc->func(apc->param1, apc->param2, apc->real_value);
+ lifo_push(&procedure_cache, apc);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+threadloop_node_t *ThreadLoop::GetAPC()
+{
+ threadloop_node_t *apc = 0;
+
+ do
+ {
+ apc = (threadloop_node_t *)lifo_pop(&procedure_cache);
+ if (!apc)
+ RefillCache();
+ } while (!apc);
+ return apc;
+}
+
+void ThreadLoop::Schedule(threadloop_node_t *apc)
+{
+ if (mpscq_push(&procedure_queue, apc) == 0)
+ ReleaseSemaphore(procedure_notification, 1, 0);
+}
+
+void ThreadLoop::Kill()
+{
+ SetEvent(kill_switch);
+}
diff --git a/Src/replicant/nu/win/ThreadLoop.h b/Src/replicant/nu/win/ThreadLoop.h
new file mode 100644
index 000000000..22f16c3da
--- /dev/null
+++ b/Src/replicant/nu/win/ThreadLoop.h
@@ -0,0 +1,40 @@
+#pragma once
+#include "nu/lfmpscq.h"
+#include "nu/LockFreeLIFO.h"
+#include <windows.h>
+
+struct threadloop_node_t : public queue_node_t
+{
+ void (*func)(void *param1, void *param2, double real_value);
+
+ void *param1;
+ void *param2;
+ double real_value;
+};
+
+class ThreadLoop
+{
+public:
+ ThreadLoop();
+ ~ThreadLoop();
+ threadloop_node_t *GetAPC(); // returns a node for you to fill out
+ void Schedule(threadloop_node_t *apc);
+ void Step();
+ void Step(unsigned int milliseconds);
+ void Run();
+ void Kill();
+private:
+ void RefillCache();
+
+ HANDLE procedure_notification;
+ HANDLE kill_switch;
+ mpscq_t procedure_queue;
+
+ /* Memory cache to be able to run APCs without having the memory manager lock
+ we'll allocate 100 at a time (#defined by PROCEDURE_CACHE_SEED)
+ and allocate new ones only if the cache is empty (which unfortunately will lock)
+ cache_bases holds the pointers we've allocated (to free on destruction of this object)
+ and procedure_cache holds the individual pointers */
+ static lifo_t procedure_cache;
+ static lifo_t cache_bases;
+}; \ No newline at end of file
diff --git a/Src/replicant/nu/x86/ByteWriter.h b/Src/replicant/nu/x86/ByteWriter.h
new file mode 100644
index 000000000..d38f0c951
--- /dev/null
+++ b/Src/replicant/nu/x86/ByteWriter.h
@@ -0,0 +1,85 @@
+#pragma once
+#include "foundation/types.h"
+#include "foundation/endian.h"
+/* this is separated out to processor-specific types for a few reasons
+
+1) Unaligned writes are fast/easy on x86, slow on some platforms (ARM, x64), very slow on a few (Itanium) and crash on others (PowerPC)
+2) ARM is very good at *ptr++, x86 is very good at ptr[offset]
+3) Endian issues
+*/
+typedef struct ByteWriter
+{
+ uint8_t *data;
+ size_t data_length;
+ size_t offset;
+} ByteWriter, *nu_bytewriter_t;
+
+/* --------- Construction & Utility --------- */
+#define BYTEWRITER_INIT(data, length) { (uint8_t *)data, length, 0 }
+
+inline static uint32_t bw_bytes_written(nu_bytewriter_t bw)
+{
+ return bw->offset;
+}
+
+/* --------- Little Endian writers --------- */
+inline static void bytewriter_fourcc_string(nu_bytewriter_t bw, const char *fourcc)
+{
+ bw->data[bw->offset] = fourcc[0];
+ bw->data[bw->offset+1] = fourcc[1];
+ bw->data[bw->offset+2] = fourcc[2];
+ bw->data[bw->offset+3] = fourcc[3];
+ bw->offset += 4;
+}
+
+inline static void bytewriter_uint32_le(nu_bytewriter_t bw, uint32_t value)
+{
+ *(uint32_t *)(&bw->data[bw->offset]) = value;
+ bw->offset+=4;
+}
+
+inline static void bytewriter_uint16_le(nu_bytewriter_t bw, uint16_t value)
+{
+ *(uint16_t *)(&bw->data[bw->offset]) = value;
+ bw->offset+=2;
+}
+
+/* --------- Big Endian writers --------- */
+inline static void bytewriter_uint32_be(nu_bytewriter_t bw, uint32_t value)
+{
+ *(uint32_t *)(&bw->data[bw->offset]) = _byteswap_ulong(value);
+ bw->offset+=4;
+}
+
+inline static void bytewriter_uint24_be(nu_bytewriter_t bw, uint32_t value)
+{
+ bw->data[bw->offset] = (uint8_t)(value >> 16) & 0xFF;
+ bw->data[bw->offset+1] = (uint8_t)(value >> 8) & 0xFF;
+ bw->data[bw->offset+2] = (uint8_t)value & 0xFF;
+ bw->offset+=3;
+}
+
+inline static void bytewriter_uint16_le(nu_bytewriter_t bw, uint16_t value)
+{
+ *(uint16_t *)(&bw->data[bw->offset]) = value;
+ bw->offset+=2;
+}
+
+/* --------- Neutral Endian writers --------- */
+inline static void bytewriter_uint32_zero(nu_bytewriter_t bw)
+{
+ *(uint32_t *)(&bw->data[bw->offset]) = 0;
+ bw->offset+=4;
+}
+
+inline static void bytewriter_uint32_nzero(nu_bytewriter_t bw, uint32_t num_zeroes)
+{
+ memset(bw->data, 0, num_zeroes*4);
+ bw->offset+=num_zeroes*4;
+}
+
+inline static void bytewriter_uint8(nu_bytewriter_t bw, uint8_t value)
+{
+ *(uint8_t *)&bw->data[bw->offset] = value;
+ bw->offset++;
+}
diff --git a/Src/replicant/nx/Android.mk b/Src/replicant/nx/Android.mk
new file mode 100644
index 000000000..48d50f0d8
--- /dev/null
+++ b/Src/replicant/nx/Android.mk
@@ -0,0 +1,27 @@
+NX_LOCAL_PATH := $(call my-dir)
+include $(NX_LOCAL_PATH)/cpufeatures/Android.mk
+
+LOCAL_PATH:= $(NX_LOCAL_PATH)
+
+
+include $(CLEAR_VARS)
+include $(ROOT_REPLICANT)/PlatformName.mk
+
+LOCAL_MODULE := nx
+LOCAL_MODULE_FILENAME := libnx.$(PLATFORM_NAME)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+LOCAL_CFLAGS := -fvisibility=hidden
+LOCAL_SRC_FILES := android/nxcondition.c android/nxstring.c android/nxuri.c android/nxmutablestring.c android/nxdata.c android/nxsemaphore.c android/nxthread.c android/nxfile.c android/nxlog.c android/NXFileObject.cpp android/NXFileProgressiveDownloader.cpp android/nxonce.c
+LOCAL_SRC_FILES += linux/nxsleep.c linux/nxpath.c
+LOCAL_SRC_FILES += cpufeatures/cpu-features.c
+
+ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
+LOCAL_SRC_FILES += android-armv7/nxonce-armv6.S
+else
+LOCAL_SRC_FILES += android-armv5/nxonce-armv5.S
+endif
+
+LOCAL_STATIC_LIBRARIES := nu foundation
+LOCAL_SHARED_LIBRARIES := nu foundation jnet
+LOCAL_LDFLAGS := -llog
+include $(BUILD_SHARED_LIBRARY)
diff --git a/Src/replicant/nx/Makefile b/Src/replicant/nx/Makefile
new file mode 100644
index 000000000..4bdea3fb2
--- /dev/null
+++ b/Src/replicant/nx/Makefile
@@ -0,0 +1,89 @@
+# ex: set ts=8 noet:
+
+MODULE_NAME := nx
+
+OS := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+
+ifeq ($(OS),Linux)
+DIR := linux
+DIRARCH := linux
+else ifeq ($(OS),Darwin)
+DIR := osx
+DIRARCH := osx-amd64
+else
+$(error OS unrecognized ($OS))
+endif
+
+CSOURCES := $(DIR)/nxstring.c $(DIR)/nxuri.c $(DIR)/nxdata.c $(DIR)/nxsemaphore.c $(DIRARCH)/nxonce.c $(DIR)/nxpath.c $(DIR)/nxcondition.c $(DIR)/nxthread.c $(DIR)/nxfile.c $(DIR)/nxsleep.c
+CPPSOURCES := $(DIR)/NXFileObject.cpp $(DIR)/NXFileProgressiveDownloader.cpp
+
+LIBRARY_FILENAME := lib$(MODULE_NAME).so
+OUTPUT_PATH := ../build/$(MODULE_NAME)
+ARCHIVE_PATH := ../build/lib
+LIBRARY_FILEPATH := ../build/lib/$(LIBRARY_FILENAME)
+
+CPPOBJS := $(patsubst %.cpp,$(OUTPUT_PATH)/%.o,$(CPPSOURCES))
+CPPDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(CPPOBJS))
+COBJS := $(patsubst %.c,$(OUTPUT_PATH)/%.o,$(CSOURCES))
+CDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(COBJS))
+
+OBJS := $(CPPOBJS) $(COBJS)
+DEPS := $(CPPDEPS) $(CDEPS)
+
+CFLAGS= -I.. -DREPLICANT_NO_ICY -DREPLICANT_NO_ULTRAVOX -DREPLICANT_NO_HTTP -D_LARGEFILE64_SOURCE -fPIC #-fvisibility=hidden
+CPPFLAGS := ${CFLAGS}
+CFLAGS += -std=gnu99 # C but not CPP, sneaky
+
+LDFLAGS =-lc -lpthread -lz #-fvisibility=hidden
+
+ifeq ($(OS),Linux)
+LDFLAGS += -lrt
+endif
+
+ifeq ($(OS),Darwin)
+CFLAGS += -F /System/Library/Frameworks/CoreServices.framework/Frameworks
+LDFLAGS += -framework CoreFoundation -framework CoreServices
+endif
+
+build: build-dir $(LIBRARY_FILEPATH)
+
+build-dir:
+ @mkdir -p $(OUTPUT_PATH)/$(DIR) > /dev/null 2> /dev/null
+ @mkdir -p $(OUTPUT_PATH)/$(DIRARCH) > /dev/null 2> /dev/null
+ @mkdir -p $(OUTPUT_PATH) > /dev/null 2> /dev/null
+ @mkdir -p $(ARCHIVE_PATH) > /dev/null 2> /dev/null
+
+dep:
+ @rm ${DEPS}
+
+$(OUTPUT_PATH)/%.o: %.cpp
+ #@echo Compiling $*.cpp
+ @$(CXX) $(CPPFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.cpp -o $(OUTPUT_PATH)/$*.o
+
+$(OUTPUT_PATH)/$(DIR)/%.o: %.cpp
+ #@echo Compiling $*.cpp
+ @$(CXX) $(CPPFLAGS) -MMD -MF $(OUTPUT_PATH)/$(DIR)/$*.d -MT $(OUTPUT_PATH)/$(DIR)/$*.o -c $*.cpp -o $(OUTPUT_PATH)/$(DIR)/$*.o
+
+$(OUTPUT_PATH)/%.o: %.c
+ #@echo Compiling $*.c
+ @$(CC) $(CFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.c -o $(OUTPUT_PATH)/$*.o
+
+$(OUTPUT_PATH)/.*/%.o: %.c
+ #@echo Compiling $*.c
+ @$(CC) $(CFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.c -o $(OUTPUT_PATH)/$(DIR)/$*.o
+
+ifeq ($(OS),Darwin)
+LIBLDFLAGS = -Wl,-install_name,libnx.so
+else
+LIBLDFLAGS = -Wl,-soname,libnx.so
+endif
+
+$(LIBRARY_FILEPATH): ${OBJS}
+ $(MAKE) -C ../nu
+ $(MAKE) -C ../jnetlib
+ @$(CXX) -Wl,-L,'../build/lib' -shared $(LIBLDFLAGS) -o $(LIBRARY_FILEPATH) ${OBJS} ${LDFLAGS} -lnu -ljnet
+
+clean:
+ -rm -f ${OBJS} $(LIBRARY_FILENAME) ${DEPS}
+
+-include $(DEPS)
diff --git a/Src/replicant/nx/VERSION b/Src/replicant/nx/VERSION
new file mode 100644
index 000000000..b966e81a4
--- /dev/null
+++ b/Src/replicant/nx/VERSION
@@ -0,0 +1 @@
+1.2.4 \ No newline at end of file
diff --git a/Src/replicant/nx/cpufeatures/Android.mk b/Src/replicant/nx/cpufeatures/Android.mk
new file mode 100644
index 000000000..ec988ba3f
--- /dev/null
+++ b/Src/replicant/nx/cpufeatures/Android.mk
@@ -0,0 +1,7 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := nullsoft-cpufeatures
+LOCAL_SRC_FILES := cpu-features.c
+LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/Src/replicant/nx/cpufeatures/cpu-features.c b/Src/replicant/nx/cpufeatures/cpu-features.c
new file mode 100644
index 000000000..488833c5d
--- /dev/null
+++ b/Src/replicant/nx/cpufeatures/cpu-features.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* ChangeLog for this library:
+ *
+ * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7
+ * for an ARMv6 CPU (see below).
+ *
+ * Handle kernels that only report 'neon', and not 'vfpv3'
+ * (VFPv3 is mandated by the ARM architecture is Neon is implemented)
+ *
+ * Handle kernels that only report 'vfpv3d16', and not 'vfpv3'
+ *
+ * Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in
+ * android_getCpuFamily().
+ *
+ * NDK r4: Initial release
+ */
+#include <sys/system_properties.h>
+#ifdef __arm__
+#include <machine/cpu-features.h>
+#endif
+#include <pthread.h>
+#include "cpu-features.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static pthread_once_t g_once;
+static AndroidCpuFamily g_cpuFamily;
+static uint64_t g_cpuFeatures;
+static int g_cpuCount;
+
+static const int android_cpufeatures_debug = 0;
+
+#ifdef __arm__
+# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM
+#elif defined __i386__
+# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86
+#else
+# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN
+#endif
+
+#define D(...) \
+ do { \
+ if (android_cpufeatures_debug) { \
+ printf(__VA_ARGS__); fflush(stdout); \
+ } \
+ } while (0)
+
+#ifdef __i386__
+static __inline__ void x86_cpuid(int func, int values[4])
+{
+ int a, b, c, d;
+ /* We need to preserve ebx since we're compiling PIC code */
+ /* this means we can't use "=b" for the second output register */
+ __asm__ __volatile__ ( \
+ "push %%ebx\n"
+ "cpuid\n" \
+ "mov %1, %%ebx\n"
+ "pop %%ebx\n"
+ : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
+ : "a" (func) \
+ );
+ values[0] = a;
+ values[1] = b;
+ values[2] = c;
+ values[3] = d;
+}
+#endif
+
+/* Read the content of /proc/cpuinfo into a user-provided buffer.
+ * Return the length of the data, or -1 on error. Does *not*
+ * zero-terminate the content. Will not read more
+ * than 'buffsize' bytes.
+ */
+static int
+read_file(const char* pathname, char* buffer, size_t buffsize)
+{
+ int fd, len;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ do {
+ len = read(fd, buffer, buffsize);
+ } while (len < 0 && errno == EINTR);
+
+ close(fd);
+
+ return len;
+}
+
+/* Extract the content of a the first occurence of a given field in
+ * the content of /proc/cpuinfo and return it as a heap-allocated
+ * string that must be freed by the caller.
+ *
+ * Return NULL if not found
+ */
+static char*
+extract_cpuinfo_field(char* buffer, int buflen, const char* field)
+{
+ int fieldlen = strlen(field);
+ char* bufend = buffer + buflen;
+ char* result = NULL;
+ int len, ignore;
+ const char *p, *q;
+
+ /* Look for first field occurence, and ensures it starts the line.
+ */
+ p = buffer;
+ bufend = buffer + buflen;
+ for (;;) {
+ p = memmem(p, bufend-p, field, fieldlen);
+ if (p == NULL)
+ goto EXIT;
+
+ if (p == buffer || p[-1] == '\n')
+ break;
+
+ p += fieldlen;
+ }
+
+ /* Skip to the first column followed by a space */
+ p += fieldlen;
+ p = memchr(p, ':', bufend-p);
+ if (p == NULL || p[1] != ' ')
+ goto EXIT;
+
+ /* Find the end of the line */
+ p += 2;
+ q = memchr(p, '\n', bufend-p);
+ if (q == NULL)
+ q = bufend;
+
+ /* Copy the line into a heap-allocated buffer */
+ len = q-p;
+ result = malloc(len+1);
+ if (result == NULL)
+ goto EXIT;
+
+ memcpy(result, p, len);
+ result[len] = '\0';
+
+EXIT:
+ return result;
+}
+
+/* Count the number of occurences of a given field prefix in /proc/cpuinfo.
+ */
+static int
+count_cpuinfo_field(char* buffer, int buflen, const char* field)
+{
+ int fieldlen = strlen(field);
+ const char* p = buffer;
+ const char* bufend = buffer + buflen;
+ const char* q;
+ int count = 0;
+
+ for (;;) {
+ const char* q;
+
+ p = memmem(p, bufend-p, field, fieldlen);
+ if (p == NULL)
+ break;
+
+ /* Ensure that the field is at the start of a line */
+ if (p > buffer && p[-1] != '\n') {
+ p += fieldlen;
+ continue;
+ }
+
+
+ /* skip any whitespace */
+ q = p + fieldlen;
+ while (q < bufend && (*q == ' ' || *q == '\t'))
+ q++;
+
+ /* we must have a colon now */
+ if (q < bufend && *q == ':') {
+ count += 1;
+ q ++;
+ }
+ p = q;
+ }
+
+ return count;
+}
+
+/* Like strlen(), but for constant string literals */
+#define STRLEN_CONST(x) ((sizeof(x)-1)
+
+
+/* Checks that a space-separated list of items contains one given 'item'.
+ * Returns 1 if found, 0 otherwise.
+ */
+static int
+has_list_item(const char* list, const char* item)
+{
+ const char* p = list;
+ int itemlen = strlen(item);
+
+ if (list == NULL)
+ return 0;
+
+ while (*p) {
+ const char* q;
+
+ /* skip spaces */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* find end of current list item */
+ q = p;
+ while (*q && *q != ' ' && *q != '\t')
+ q++;
+
+ if (itemlen == q-p && !memcmp(p, item, itemlen))
+ return 1;
+
+ /* skip to next item */
+ p = q;
+ }
+ return 0;
+}
+
+
+static void
+android_cpuInit(void)
+{
+ char cpuinfo[4096];
+ int cpuinfo_len;
+
+ g_cpuFamily = DEFAULT_CPU_FAMILY;
+ g_cpuFeatures = 0;
+ g_cpuCount = 1;
+
+ cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
+ D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
+ cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
+
+ if (cpuinfo_len < 0) /* should not happen */ {
+ return;
+ }
+
+ /* Count the CPU cores, the value may be 0 for single-core CPUs */
+ g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
+ if (g_cpuCount == 0) {
+ g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
+ if (g_cpuCount == 0) {
+ g_cpuCount = 1;
+ }
+ }
+
+ D("found cpuCount = %d\n", g_cpuCount);
+
+#ifdef __ARM_ARCH__
+ {
+ char* features = NULL;
+ char* architecture = NULL;
+
+ /* Extract architecture from the "CPU Architecture" field.
+ * The list is well-known, unlike the the output of
+ * the 'Processor' field which can vary greatly.
+ *
+ * See the definition of the 'proc_arch' array in
+ * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
+ * same file.
+ */
+ char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
+
+ if (cpuArch != NULL) {
+ char* end;
+ long archNumber;
+ int hasARMv7 = 0;
+
+ D("found cpuArch = '%s'\n", cpuArch);
+
+ /* read the initial decimal number, ignore the rest */
+ archNumber = strtol(cpuArch, &end, 10);
+
+ /* Here we assume that ARMv8 will be upwards compatible with v7
+ * in the future. Unfortunately, there is no 'Features' field to
+ * indicate that Thumb-2 is supported.
+ */
+ if (end > cpuArch && archNumber >= 7) {
+ hasARMv7 = 1;
+ }
+
+ /* Unfortunately, it seems that certain ARMv6-based CPUs
+ * report an incorrect architecture number of 7!
+ *
+ * See http://code.google.com/p/android/issues/detail?id=10812
+ *
+ * We try to correct this by looking at the 'elf_format'
+ * field reported by the 'Processor' field, which is of the
+ * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
+ * an ARMv6-one.
+ */
+ if (hasARMv7) {
+ char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
+ "Processor");
+ if (cpuProc != NULL) {
+ D("found cpuProc = '%s'\n", cpuProc);
+ if (has_list_item(cpuProc, "(v6l)")) {
+ D("CPU processor and architecture mismatch!!\n");
+ hasARMv7 = 0;
+ }
+ free(cpuProc);
+ }
+ }
+
+ if (hasARMv7) {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
+ }
+
+ /* The LDREX / STREX instructions are available from ARMv6 */
+ if (archNumber >= 6) {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
+ }
+
+ free(cpuArch);
+ }
+
+ /* Extract the list of CPU features from 'Features' field */
+ char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
+
+ if (cpuFeatures != NULL) {
+
+ D("found cpuFeatures = '%s'\n", cpuFeatures);
+
+ if (has_list_item(cpuFeatures, "vfp"))
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFP;
+
+ if (has_list_item(cpuFeatures, "vfpv3"))
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
+
+ else if (has_list_item(cpuFeatures, "vfpv3d16"))
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
+
+ if (has_list_item(cpuFeatures, "neon")) {
+ /* Note: Certain kernels only report neon but not vfpv3
+ * in their features list. However, ARM mandates
+ * that if Neon is implemented, so must be VFPv3
+ * so always set the flag.
+ */
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON |
+ ANDROID_CPU_ARM_FEATURE_VFPv3;
+ }
+ free(cpuFeatures);
+ }
+ }
+#endif /* __ARM_ARCH__ */
+
+#ifdef __i386__
+ g_cpuFamily = ANDROID_CPU_FAMILY_X86;
+
+ int regs[4];
+
+/* According to http://en.wikipedia.org/wiki/CPUID */
+#define VENDOR_INTEL_b 0x756e6547
+#define VENDOR_INTEL_c 0x6c65746e
+#define VENDOR_INTEL_d 0x49656e69
+
+ x86_cpuid(0, regs);
+ int vendorIsIntel = (regs[1] == VENDOR_INTEL_b &&
+ regs[2] == VENDOR_INTEL_c &&
+ regs[3] == VENDOR_INTEL_d);
+
+ x86_cpuid(1, regs);
+ if ((regs[2] & (1 << 9)) != 0) {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3;
+ }
+ if ((regs[2] & (1 << 23)) != 0) {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT;
+ }
+ if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) {
+ g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE;
+ }
+#endif
+}
+
+
+AndroidCpuFamily
+android_getCpuFamily(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuFamily;
+}
+
+
+uint64_t
+android_getCpuFeatures(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuFeatures;
+}
+
+
+int
+android_getCpuCount(void)
+{
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuCount;
+}
diff --git a/Src/replicant/nx/cpufeatures/cpu-features.h b/Src/replicant/nx/cpufeatures/cpu-features.h
new file mode 100644
index 000000000..ef7241748
--- /dev/null
+++ b/Src/replicant/nx/cpufeatures/cpu-features.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef CPU_FEATURES_H
+#define CPU_FEATURES_H
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+typedef enum {
+ ANDROID_CPU_FAMILY_UNKNOWN = 0,
+ ANDROID_CPU_FAMILY_ARM,
+ ANDROID_CPU_FAMILY_X86,
+
+ ANDROID_CPU_FAMILY_MAX /* do not remove */
+
+} AndroidCpuFamily;
+
+/* Return family of the device's CPU */
+__attribute__ ((visibility("default"))) extern AndroidCpuFamily android_getCpuFamily(void);
+
+enum {
+ ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0),
+ ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1),
+ ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2),
+ ANDROID_CPU_ARM_FEATURE_LDREX_STREX = (1 << 3),
+ ANDROID_CPU_ARM_FEATURE_ARMv6=ANDROID_CPU_ARM_FEATURE_LDREX_STREX,
+ ANDROID_CPU_ARM_FEATURE_VFP = (1 << 4),
+};
+
+enum {
+ ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0),
+ ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1),
+ ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2),
+};
+
+__attribute__ ((visibility("default"))) extern uint64_t android_getCpuFeatures(void);
+
+/* Return the number of CPU cores detected on this device. */
+__attribute__ ((visibility("default"))) extern int android_getCpuCount(void);
+
+__END_DECLS
+
+#endif /* CPU_FEATURES_H */
diff --git a/Src/replicant/nx/nx.h b/Src/replicant/nx/nx.h
new file mode 100644
index 000000000..19bf6efe8
--- /dev/null
+++ b/Src/replicant/nx/nx.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "nx/nxapi.h"
+#include "nx/nxcondition.h"
+#include "nx/nxdata.h"
+#include "nx/nxfile.h"
+#include "nx/nxmutablestring.h"
+#include "nx/nxonce.h"
+#include "nx/nxpath.h"
+#include "nx/nxsemaphore.h"
+#include "nx/nxsleep.h"
+#include "nx/nxstring.h"
+#include "nx/nxthread.h"
+#include "nx/nxuri.h"
+#include "nx/nxtime.h"
diff --git a/Src/replicant/nx/nx.rc b/Src/replicant/nx/nx.rc
new file mode 100644
index 000000000..fcff77115
--- /dev/null
+++ b/Src/replicant/nx/nx.rc
@@ -0,0 +1,76 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/replicant/nx/nx.sln b/Src/replicant/nx/nx.sln
new file mode 100644
index 000000000..233d259ed
--- /dev/null
+++ b/Src/replicant/nx/nx.sln
@@ -0,0 +1,67 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "nx.vcxproj", "{2851CF33-337D-44D9-BA6D-30547B1CDEF0}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Debug|Win32.ActiveCfg = Debug|Win32
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Debug|Win32.Build.0 = Debug|Win32
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Debug|x64.ActiveCfg = Debug|x64
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Debug|x64.Build.0 = Debug|x64
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Release|Win32.ActiveCfg = Release|Win32
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Release|Win32.Build.0 = Release|Win32
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Release|x64.ActiveCfg = Release|x64
+ {2851CF33-337D-44D9-BA6D-30547B1CDEF0}.Release|x64.Build.0 = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64
+ {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64
+ {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64
+ {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/nx/nx.vcxproj b/Src/replicant/nx/nx.vcxproj
new file mode 100644
index 000000000..2dc85a2dd
--- /dev/null
+++ b/Src/replicant/nx/nx.vcxproj
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{57C90706-B25D-4ACA-9B33-95CDB2427C27}</ProjectGuid>
+ <RootNamespace>nx</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>nxlite</TargetName>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>nxlite</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>nxlite</TargetName>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>nxlite</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NX_EXPORTS;_WIN32_WINNT=0x601;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;6011;6031;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>jnetlib.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(ProjectDir)x86_Debug\$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;NX_EXPORTS;_WIN32_WINNT=0x601;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;6011;6031;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>jnetlib.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(ProjectDir)x64_Debug\$(ProjectName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;NX_EXPORTS;_WIN32_WINNT=0x601;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;4244;4355;6011;6031;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>jnetlib.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <ImportLibrary>$(ProjectDir)x86_Release\$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;NX_EXPORTS;_WIN32_WINNT=0x601;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;4244;4355;6011;6031;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>jnetlib.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <ImportLibrary>$(ProjectDir)x64_Release\$(ProjectName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="win\nxdata.c" />
+ <ClCompile Include="win\nxfile.c" />
+ <ClCompile Include="win\NXFileObject.cpp" />
+ <ClCompile Include="win\NXFileProgressiveDownloader.cpp" />
+ <ClCompile Include="win\nxonce.c" />
+ <ClCompile Include="win\nxpath.c" />
+ <ClCompile Include="win\nxsleep.c" />
+ <ClCompile Include="win\nxstring.c" />
+ <ClCompile Include="win\nxthread.c" />
+ <ClCompile Include="win\nxuri.c" />
+ <ClCompile Include="win\NXZipFile.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\foundation\error.h" />
+ <ClInclude Include="..\foundation\guid.h" />
+ <ClInclude Include="..\foundation\types.h" />
+ <ClInclude Include="nxapi.h" />
+ <ClInclude Include="nxstring.h" />
+ <ClInclude Include="nxuri.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="win\nxonce.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="nx.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\WAT\WAT.vcxproj">
+ <Project>{c5714908-a71f-4644-bd95-aad8ee7914da}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\jnetlib\jnetlib.vcxproj">
+ <Project>{e105a0a2-7391-47c5-86ac-718003524c3d}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nx/nx.vcxproj.filters b/Src/replicant/nx/nx.vcxproj.filters
new file mode 100644
index 000000000..e2e495eb3
--- /dev/null
+++ b/Src/replicant/nx/nx.vcxproj.filters
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="win\nxdata.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxfile.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\NXFileObject.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\NXFileProgressiveDownloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxonce.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxpath.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxsleep.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxstring.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxthread.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\nxuri.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="win\NXZipFile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\foundation\error.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\foundation\guid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="win\nxonce.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\foundation\types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="nxstring.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="nxuri.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="nxapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{e3ece7c2-5453-4ab4-9b86-9c6e0ea664d9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{fe5588c0-7153-48f2-b1ba-c2406e319412}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{44167388-5010-436c-8849-1fc629ca5549}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="nx.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/nx/nx.xcodeproj/project.pbxproj b/Src/replicant/nx/nx.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..3c80eb9d0
--- /dev/null
+++ b/Src/replicant/nx/nx.xcodeproj/project.pbxproj
@@ -0,0 +1,802 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 00B73401151BADB700A8251C /* nx-prepare */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 00B73402151BADB700A8251C /* Build configuration list for PBXAggregateTarget "nx-prepare" */;
+ buildPhases = (
+ 00B73405151BADCC00A8251C /* Generate Version Info */,
+ );
+ dependencies = (
+ 0039B369152A1F3500D96D3E /* PBXTargetDependency */,
+ );
+ name = "nx-prepare";
+ productName = "nx-version";
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 00512B97156BD61500115B09 /* nxtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 00512B96156BD61500115B09 /* nxtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00512B9A156BD62800115B09 /* nxtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 00512B99156BD62800115B09 /* nxtime.h */; };
+ 00512B9B156BD66500115B09 /* nxtime.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 00512B99156BD62800115B09 /* nxtime.h */; };
+ 0062CA5B170B228300CBFD15 /* MessageLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0062CA59170B228300CBFD15 /* MessageLoop.cpp */; };
+ 0062CA5C170B228300CBFD15 /* MessageLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = 0062CA5A170B228300CBFD15 /* MessageLoop.h */; };
+ 00695D1816E70C1A009CB115 /* NXFileProgressiveDownloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00695D1516E70C1A009CB115 /* NXFileProgressiveDownloader.cpp */; };
+ 00695D1916E70C1A009CB115 /* nxlog.c in Sources */ = {isa = PBXBuildFile; fileRef = 00695D1616E70C1A009CB115 /* nxlog.c */; };
+ 00695D1A16E70C1A009CB115 /* nxlog.h in Headers */ = {isa = PBXBuildFile; fileRef = 00695D1716E70C1A009CB115 /* nxlog.h */; };
+ 00695D1E16E70D19009CB115 /* nxlog.h in Headers */ = {isa = PBXBuildFile; fileRef = 00695D1D16E70D19009CB115 /* nxlog.h */; };
+ 00695D2016E70D2B009CB115 /* NXFileObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 00695D1F16E70D2B009CB115 /* NXFileObject.h */; };
+ 00695D2E16E7DEB5009CB115 /* libjnetlib-1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00695D2D16E7DEB5009CB115 /* libjnetlib-1.dylib */; };
+ 00695D4316E86E28009CB115 /* nxlog.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 00695D1716E70C1A009CB115 /* nxlog.h */; };
+ 00723DD5150E8CCD006847F2 /* nxfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 00723DD3150E8CCD006847F2 /* nxfile.c */; };
+ 00723DD6150E8CCD006847F2 /* nxfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 00723DD4150E8CCD006847F2 /* nxfile.h */; };
+ 00723DD8150E8CF0006847F2 /* nxfile.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 00723DD4150E8CCD006847F2 /* nxfile.h */; };
+ 00723DDA150E8D86006847F2 /* nxsleep.h in Headers */ = {isa = PBXBuildFile; fileRef = 00723DD9150E8D86006847F2 /* nxsleep.h */; };
+ 00723DDB150E8D9C006847F2 /* nxsleep.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 00723DD9150E8D86006847F2 /* nxsleep.h */; };
+ 0074C89E1679008C005E2369 /* NXFileObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0074C89D1679008C005E2369 /* NXFileObject.cpp */; };
+ 0074C90016790DBD005E2369 /* libnu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0074C8FF16790DBD005E2369 /* libnu.a */; };
+ 008840A315064F2E00625F51 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5DCD1A14BB5E4D0077EB3A /* CoreFoundation.framework */; };
+ 008840FE1506575D00625F51 /* nxapi.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 0C80A05614B64F0400FAA6BA /* nxapi.h */; };
+ 008840FF1506575D00625F51 /* nxcondition.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 0CC9985E14C5DBC200484291 /* nxcondition.h */; };
+ 008841001506575D00625F51 /* nxdata.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = B1FE9B9214F5E29000629941 /* nxdata.h */; };
+ 008841011506575D00625F51 /* nxpath.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 0C5DCD1D14BB67C70077EB3A /* nxpath.h */; };
+ 008841021506575D00625F51 /* nxstring.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 0C80A04F14B64EC900FAA6BA /* nxstring.h */; };
+ 008841031506575D00625F51 /* nxthread.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 0C80A05114B64EC900FAA6BA /* nxthread.h */; };
+ 008841041506575D00625F51 /* nxuri.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 0C5494D114BB8543005EFB72 /* nxuri.h */; };
+ 0088410F150657D000625F51 /* nx.h in Headers */ = {isa = PBXBuildFile; fileRef = 00884105150657CF00625F51 /* nx.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884110150657D000625F51 /* nxapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 00884106150657CF00625F51 /* nxapi.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884111150657D000625F51 /* nxcondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 00884107150657CF00625F51 /* nxcondition.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884112150657D000625F51 /* nxdata.h in Headers */ = {isa = PBXBuildFile; fileRef = 00884108150657CF00625F51 /* nxdata.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884113150657D000625F51 /* nxfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 00884109150657CF00625F51 /* nxfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884114150657D000625F51 /* nxmutablestring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0088410A150657CF00625F51 /* nxmutablestring.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884115150657D000625F51 /* nxpath.h in Headers */ = {isa = PBXBuildFile; fileRef = 0088410B150657CF00625F51 /* nxpath.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884116150657D000625F51 /* nxsleep.h in Headers */ = {isa = PBXBuildFile; fileRef = 0088410C150657CF00625F51 /* nxsleep.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884117150657D000625F51 /* nxstring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0088410D150657D000625F51 /* nxstring.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884118150657D000625F51 /* nxuri.h in Headers */ = {isa = PBXBuildFile; fileRef = 0088410E150657D000625F51 /* nxuri.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00884119150657E600625F51 /* nxonce.h in Copy Mac OS X amd64 Public Headers */ = {isa = PBXBuildFile; fileRef = 0C80A06D14B6575700FAA6BA /* nxonce.h */; };
+ 0088411A150657EC00625F51 /* nxonce.h in Copy Mac OS X x86 Public Headers */ = {isa = PBXBuildFile; fileRef = 0C80A08214B65B5000FAA6BA /* nxonce.h */; };
+ 00B732B4151A625D00A8251C /* nxsemaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B732B3151A625D00A8251C /* nxsemaphore.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00B732B8151A628A00A8251C /* nxsemaphore.c in Sources */ = {isa = PBXBuildFile; fileRef = 00B732B6151A628A00A8251C /* nxsemaphore.c */; };
+ 00B732B9151A628A00A8251C /* nxsemaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = 00B732B7151A628A00A8251C /* nxsemaphore.h */; };
+ 00B732BA151A62F300A8251C /* nxsemaphore.h in Copy Mac OS X Public Headers */ = {isa = PBXBuildFile; fileRef = 00B732B7151A628A00A8251C /* nxsemaphore.h */; };
+ 00C27E901537245F008D95CD /* precomp.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C27E8F1537245B008D95CD /* precomp.h */; };
+ 00ED0A4617A28E1400BBA8B0 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00ED0A4517A28E1300BBA8B0 /* CoreServices.framework */; };
+ 00F8A4F3150F0B9700CB104E /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 00F8A4F1150F0B9700CB104E /* version.c */; };
+ 00F8A4F4150F0B9700CB104E /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 00F8A4F2150F0B9700CB104E /* version.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 0C5494CF14BB8514005EFB72 /* nxuri.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C5494CE14BB8514005EFB72 /* nxuri.c */; };
+ 0C5494D214BB8543005EFB72 /* nxuri.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C5494D114BB8543005EFB72 /* nxuri.h */; settings = {ATTRIBUTES = (); }; };
+ 0C5DCD1914BB5DA40077EB3A /* nxstring.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C5DCD1814BB5DA40077EB3A /* nxstring.c */; };
+ 0C5DCD1E14BB67C70077EB3A /* nxpath.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C5DCD1C14BB67C70077EB3A /* nxpath.c */; };
+ 0C5DCD1F14BB67C70077EB3A /* nxpath.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C5DCD1D14BB67C70077EB3A /* nxpath.h */; settings = {ATTRIBUTES = (); }; };
+ 0C80A05214B64EC900FAA6BA /* nxstring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C80A04F14B64EC900FAA6BA /* nxstring.h */; settings = {ATTRIBUTES = (); }; };
+ 0C80A05314B64EC900FAA6BA /* nxthread.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C80A05014B64EC900FAA6BA /* nxthread.c */; };
+ 0C80A05414B64EC900FAA6BA /* nxthread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C80A05114B64EC900FAA6BA /* nxthread.h */; settings = {ATTRIBUTES = (); }; };
+ 0C80A05914B64F6400FAA6BA /* nxthread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C80A05814B64F6400FAA6BA /* nxthread.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 0C80A06F14B6575700FAA6BA /* nxonce.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C80A06D14B6575700FAA6BA /* nxonce.h */; };
+ 0C80A08014B658C000FAA6BA /* nxonce.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C80A07F14B658C000FAA6BA /* nxonce.c */; };
+ 0C80A08314B65B5000FAA6BA /* nxonce.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C80A08114B65B5000FAA6BA /* nxonce.c */; };
+ 0C80A08414B65B5000FAA6BA /* nxonce.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C80A08214B65B5000FAA6BA /* nxonce.h */; };
+ 0C80A08A14B65C6800FAA6BA /* nxonce.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C80A08914B65C6800FAA6BA /* nxonce.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 0CC9985F14C5DBC200484291 /* nxcondition.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CC9985D14C5DBC200484291 /* nxcondition.c */; };
+ 0CC9986014C5DBC200484291 /* nxcondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CC9985E14C5DBC200484291 /* nxcondition.h */; settings = {ATTRIBUTES = (); }; };
+ B180783C14F48398000B9733 /* nxsleep.c in Sources */ = {isa = PBXBuildFile; fileRef = B180783B14F48398000B9733 /* nxsleep.c */; };
+ B1FE9B9314F5E29000629941 /* nxdata.c in Sources */ = {isa = PBXBuildFile; fileRef = B1FE9B9114F5E29000629941 /* nxdata.c */; };
+ B1FE9B9414F5E29000629941 /* nxdata.h in Headers */ = {isa = PBXBuildFile; fileRef = B1FE9B9214F5E29000629941 /* nxdata.h */; settings = {ATTRIBUTES = (); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 002FD394151981BC00857D4F /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0CC19BF514B63FDC0095FCAE /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 002FD390151981B200857D4F;
+ remoteInfo = "nx-cleanup";
+ };
+ 0039B368152A1F3500D96D3E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0CC19BF514B63FDC0095FCAE /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 002FD390151981B200857D4F;
+ remoteInfo = "nx-cleanup";
+ };
+ 00B73407151BAE0C00A8251C /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0CC19BF514B63FDC0095FCAE /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00B73401151BADB700A8251C;
+ remoteInfo = "nx-version";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 008840FB150656FD00625F51 /* Copy Mac OS X Public Headers */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 12;
+ dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx";
+ dstSubfolderSpec = 16;
+ files = (
+ 00512B9B156BD66500115B09 /* nxtime.h in Copy Mac OS X Public Headers */,
+ 00723DDB150E8D9C006847F2 /* nxsleep.h in Copy Mac OS X Public Headers */,
+ 00723DD8150E8CF0006847F2 /* nxfile.h in Copy Mac OS X Public Headers */,
+ 008840FE1506575D00625F51 /* nxapi.h in Copy Mac OS X Public Headers */,
+ 008840FF1506575D00625F51 /* nxcondition.h in Copy Mac OS X Public Headers */,
+ 008841001506575D00625F51 /* nxdata.h in Copy Mac OS X Public Headers */,
+ 00695D4316E86E28009CB115 /* nxlog.h in Copy Mac OS X Public Headers */,
+ 008841011506575D00625F51 /* nxpath.h in Copy Mac OS X Public Headers */,
+ 00B732BA151A62F300A8251C /* nxsemaphore.h in Copy Mac OS X Public Headers */,
+ 008841021506575D00625F51 /* nxstring.h in Copy Mac OS X Public Headers */,
+ 008841031506575D00625F51 /* nxthread.h in Copy Mac OS X Public Headers */,
+ 008841041506575D00625F51 /* nxuri.h in Copy Mac OS X Public Headers */,
+ );
+ name = "Copy Mac OS X Public Headers";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 008840FC1506572100625F51 /* Copy Mac OS X x86 Public Headers */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 12;
+ dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx-x86";
+ dstSubfolderSpec = 16;
+ files = (
+ 0088411A150657EC00625F51 /* nxonce.h in Copy Mac OS X x86 Public Headers */,
+ );
+ name = "Copy Mac OS X x86 Public Headers";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 008840FD1506572B00625F51 /* Copy Mac OS X amd64 Public Headers */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 12;
+ dstPath = "$(PUBLIC_HEADERS_FOLDER_PATH)/osx-amd64";
+ dstSubfolderSpec = 16;
+ files = (
+ 00884119150657E600625F51 /* nxonce.h in Copy Mac OS X amd64 Public Headers */,
+ );
+ name = "Copy Mac OS X amd64 Public Headers";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 00512B96156BD61500115B09 /* nxtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxtime.h; sourceTree = "<group>"; };
+ 00512B99156BD62800115B09 /* nxtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxtime.h; path = osx/nxtime.h; sourceTree = "<group>"; };
+ 0062CA59170B228300CBFD15 /* MessageLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MessageLoop.cpp; path = ../nu/osx/MessageLoop.cpp; sourceTree = "<group>"; };
+ 0062CA5A170B228300CBFD15 /* MessageLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MessageLoop.h; path = ../nu/osx/MessageLoop.h; sourceTree = "<group>"; };
+ 00695D1516E70C1A009CB115 /* NXFileProgressiveDownloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NXFileProgressiveDownloader.cpp; path = osx/NXFileProgressiveDownloader.cpp; sourceTree = "<group>"; };
+ 00695D1616E70C1A009CB115 /* nxlog.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxlog.c; path = osx/nxlog.c; sourceTree = "<group>"; };
+ 00695D1716E70C1A009CB115 /* nxlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxlog.h; path = osx/nxlog.h; sourceTree = "<group>"; };
+ 00695D1D16E70D19009CB115 /* nxlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxlog.h; sourceTree = "<group>"; };
+ 00695D1F16E70D2B009CB115 /* NXFileObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NXFileObject.h; path = osx/NXFileObject.h; sourceTree = "<group>"; };
+ 00695D2D16E7DEB5009CB115 /* libjnetlib-1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = "libjnetlib-1.dylib"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 00723DD3150E8CCD006847F2 /* nxfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxfile.c; path = osx/nxfile.c; sourceTree = "<group>"; };
+ 00723DD4150E8CCD006847F2 /* nxfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxfile.h; path = osx/nxfile.h; sourceTree = "<group>"; };
+ 00723DD9150E8D86006847F2 /* nxsleep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxsleep.h; path = osx/nxsleep.h; sourceTree = "<group>"; };
+ 0073AB96150A7A7C0027A889 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
+ 0074C89D1679008C005E2369 /* NXFileObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NXFileObject.cpp; path = osx/NXFileObject.cpp; sourceTree = "<group>"; };
+ 0074C8FF16790DBD005E2369 /* libnu.a */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = libnu.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 00884105150657CF00625F51 /* nx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nx.h; sourceTree = "<group>"; };
+ 00884106150657CF00625F51 /* nxapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxapi.h; sourceTree = "<group>"; };
+ 00884107150657CF00625F51 /* nxcondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxcondition.h; sourceTree = "<group>"; };
+ 00884108150657CF00625F51 /* nxdata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxdata.h; sourceTree = "<group>"; };
+ 00884109150657CF00625F51 /* nxfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxfile.h; sourceTree = "<group>"; };
+ 0088410A150657CF00625F51 /* nxmutablestring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxmutablestring.h; sourceTree = "<group>"; };
+ 0088410B150657CF00625F51 /* nxpath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxpath.h; sourceTree = "<group>"; };
+ 0088410C150657CF00625F51 /* nxsleep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxsleep.h; sourceTree = "<group>"; };
+ 0088410D150657D000625F51 /* nxstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxstring.h; sourceTree = "<group>"; };
+ 0088410E150657D000625F51 /* nxuri.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxuri.h; sourceTree = "<group>"; };
+ 0092A7AA17A30C6400E0ECD8 /* CarbonCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CarbonCore.framework; path = ../../../../../System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework; sourceTree = "<group>"; };
+ 00B732B3151A625D00A8251C /* nxsemaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxsemaphore.h; sourceTree = "<group>"; };
+ 00B732B6151A628A00A8251C /* nxsemaphore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxsemaphore.c; path = osx/nxsemaphore.c; sourceTree = "<group>"; };
+ 00B732B7151A628A00A8251C /* nxsemaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxsemaphore.h; path = osx/nxsemaphore.h; sourceTree = "<group>"; };
+ 00C27E8F1537245B008D95CD /* precomp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = precomp.h; sourceTree = "<group>"; };
+ 00ED0A4517A28E1300BBA8B0 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
+ 00F8A4F1150F0B9700CB104E /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = version.c; path = $PROJECT_DERIVED_FILE_DIR/version.c; sourceTree = "<absolute>"; };
+ 00F8A4F2150F0B9700CB104E /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = version.h; path = $PROJECT_DERIVED_FILE_DIR/version.h; sourceTree = "<absolute>"; };
+ 0C5494CE14BB8514005EFB72 /* nxuri.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxuri.c; path = osx/nxuri.c; sourceTree = "<group>"; };
+ 0C5494D114BB8543005EFB72 /* nxuri.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxuri.h; path = osx/nxuri.h; sourceTree = "<group>"; };
+ 0C5DCD1814BB5DA40077EB3A /* nxstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxstring.c; path = osx/nxstring.c; sourceTree = "<group>"; };
+ 0C5DCD1A14BB5E4D0077EB3A /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+ 0C5DCD1C14BB67C70077EB3A /* nxpath.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxpath.c; path = osx/nxpath.c; sourceTree = "<group>"; };
+ 0C5DCD1D14BB67C70077EB3A /* nxpath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxpath.h; path = osx/nxpath.h; sourceTree = "<group>"; };
+ 0C80A04F14B64EC900FAA6BA /* nxstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxstring.h; path = osx/nxstring.h; sourceTree = "<group>"; };
+ 0C80A05014B64EC900FAA6BA /* nxthread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxthread.c; path = osx/nxthread.c; sourceTree = "<group>"; };
+ 0C80A05114B64EC900FAA6BA /* nxthread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxthread.h; path = osx/nxthread.h; sourceTree = "<group>"; };
+ 0C80A05614B64F0400FAA6BA /* nxapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxapi.h; path = osx/nxapi.h; sourceTree = "<group>"; };
+ 0C80A05814B64F6400FAA6BA /* nxthread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxthread.h; sourceTree = "<group>"; };
+ 0C80A06D14B6575700FAA6BA /* nxonce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxonce.h; path = "osx-amd64/nxonce.h"; sourceTree = "<group>"; };
+ 0C80A07F14B658C000FAA6BA /* nxonce.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxonce.c; path = "osx-amd64/nxonce.c"; sourceTree = "<group>"; };
+ 0C80A08114B65B5000FAA6BA /* nxonce.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxonce.c; path = "osx-x86/nxonce.c"; sourceTree = "<group>"; };
+ 0C80A08214B65B5000FAA6BA /* nxonce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxonce.h; path = "osx-x86/nxonce.h"; sourceTree = "<group>"; };
+ 0C80A08914B65C6800FAA6BA /* nxonce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nxonce.h; sourceTree = "<group>"; };
+ 0CC19BFE14B63FDC0095FCAE /* libnx-1.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libnx-1.dylib"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 0CC9985D14C5DBC200484291 /* nxcondition.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxcondition.c; path = osx/nxcondition.c; sourceTree = "<group>"; };
+ 0CC9985E14C5DBC200484291 /* nxcondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxcondition.h; path = osx/nxcondition.h; sourceTree = "<group>"; };
+ B180783B14F48398000B9733 /* nxsleep.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxsleep.c; path = osx/nxsleep.c; sourceTree = "<group>"; };
+ B1FE9B9114F5E29000629941 /* nxdata.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nxdata.c; path = osx/nxdata.c; sourceTree = "<group>"; };
+ B1FE9B9214F5E29000629941 /* nxdata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nxdata.h; path = osx/nxdata.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 0CC19BFB14B63FDC0095FCAE /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0074C90016790DBD005E2369 /* libnu.a in Frameworks */,
+ 00695D2E16E7DEB5009CB115 /* libjnetlib-1.dylib in Frameworks */,
+ 008840A315064F2E00625F51 /* CoreFoundation.framework in Frameworks */,
+ 00ED0A4617A28E1400BBA8B0 /* CoreServices.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 0062CA58170B226C00CBFD15 /* nu */ = {
+ isa = PBXGroup;
+ children = (
+ 0062CA59170B228300CBFD15 /* MessageLoop.cpp */,
+ 0062CA5A170B228300CBFD15 /* MessageLoop.h */,
+ );
+ name = nu;
+ sourceTree = "<group>";
+ };
+ 0074C8FD16790D81005E2369 /* Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 0074C8FF16790DBD005E2369 /* libnu.a */,
+ 00695D2D16E7DEB5009CB115 /* libjnetlib-1.dylib */,
+ );
+ name = Libraries;
+ sourceTree = "<group>";
+ };
+ 008840A215064EE800625F51 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 0C5DCD1A14BB5E4D0077EB3A /* CoreFoundation.framework */,
+ 00ED0A4517A28E1300BBA8B0 /* CoreServices.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 00F8A4F0150F0AFF00CB104E /* Version */ = {
+ isa = PBXGroup;
+ children = (
+ 0073AB96150A7A7C0027A889 /* VERSION */,
+ 00F8A4F1150F0B9700CB104E /* version.c */,
+ 00F8A4F2150F0B9700CB104E /* version.h */,
+ );
+ name = Version;
+ sourceTree = "<group>";
+ };
+ 0C80A04E14B64EB800FAA6BA /* osx */ = {
+ isa = PBXGroup;
+ children = (
+ 0C80A05614B64F0400FAA6BA /* nxapi.h */,
+ 0CC9985D14C5DBC200484291 /* nxcondition.c */,
+ 0CC9985E14C5DBC200484291 /* nxcondition.h */,
+ B1FE9B9114F5E29000629941 /* nxdata.c */,
+ B1FE9B9214F5E29000629941 /* nxdata.h */,
+ 00723DD3150E8CCD006847F2 /* nxfile.c */,
+ 00723DD4150E8CCD006847F2 /* nxfile.h */,
+ 0074C89D1679008C005E2369 /* NXFileObject.cpp */,
+ 00695D1F16E70D2B009CB115 /* NXFileObject.h */,
+ 00695D1516E70C1A009CB115 /* NXFileProgressiveDownloader.cpp */,
+ 00695D1616E70C1A009CB115 /* nxlog.c */,
+ 00695D1716E70C1A009CB115 /* nxlog.h */,
+ 0C5DCD1C14BB67C70077EB3A /* nxpath.c */,
+ 0C5DCD1D14BB67C70077EB3A /* nxpath.h */,
+ 00B732B6151A628A00A8251C /* nxsemaphore.c */,
+ 00B732B7151A628A00A8251C /* nxsemaphore.h */,
+ B180783B14F48398000B9733 /* nxsleep.c */,
+ 00723DD9150E8D86006847F2 /* nxsleep.h */,
+ 0C5DCD1814BB5DA40077EB3A /* nxstring.c */,
+ 0C80A04F14B64EC900FAA6BA /* nxstring.h */,
+ 0C80A05014B64EC900FAA6BA /* nxthread.c */,
+ 0C80A05114B64EC900FAA6BA /* nxthread.h */,
+ 00512B99156BD62800115B09 /* nxtime.h */,
+ 0C5494CE14BB8514005EFB72 /* nxuri.c */,
+ 0C5494D114BB8543005EFB72 /* nxuri.h */,
+ );
+ name = osx;
+ sourceTree = "<group>";
+ };
+ 0CC19BF314B63FDC0095FCAE = {
+ isa = PBXGroup;
+ children = (
+ 0092A7AA17A30C6400E0ECD8 /* CarbonCore.framework */,
+ 00695D1D16E70D19009CB115 /* nxlog.h */,
+ 00512B96156BD61500115B09 /* nxtime.h */,
+ 00C27E8F1537245B008D95CD /* precomp.h */,
+ 00884105150657CF00625F51 /* nx.h */,
+ 00884106150657CF00625F51 /* nxapi.h */,
+ 00884107150657CF00625F51 /* nxcondition.h */,
+ 00884108150657CF00625F51 /* nxdata.h */,
+ 00884109150657CF00625F51 /* nxfile.h */,
+ 0088410A150657CF00625F51 /* nxmutablestring.h */,
+ 0C80A08914B65C6800FAA6BA /* nxonce.h */,
+ 0088410B150657CF00625F51 /* nxpath.h */,
+ 0088410C150657CF00625F51 /* nxsleep.h */,
+ 00B732B3151A625D00A8251C /* nxsemaphore.h */,
+ 0088410D150657D000625F51 /* nxstring.h */,
+ 0C80A05814B64F6400FAA6BA /* nxthread.h */,
+ 0088410E150657D000625F51 /* nxuri.h */,
+ 0C80A04E14B64EB800FAA6BA /* osx */,
+ 0CC19C1414B640810095FCAE /* amd64 */,
+ 0CC19C1314B6407A0095FCAE /* x86 */,
+ 0062CA58170B226C00CBFD15 /* nu */,
+ 00F8A4F0150F0AFF00CB104E /* Version */,
+ 0074C8FD16790D81005E2369 /* Libraries */,
+ 008840A215064EE800625F51 /* Frameworks */,
+ 0CC19BFF14B63FDC0095FCAE /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 0CC19BFF14B63FDC0095FCAE /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 0CC19BFE14B63FDC0095FCAE /* libnx-1.dylib */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 0CC19C1314B6407A0095FCAE /* x86 */ = {
+ isa = PBXGroup;
+ children = (
+ 0C80A08114B65B5000FAA6BA /* nxonce.c */,
+ 0C80A08214B65B5000FAA6BA /* nxonce.h */,
+ );
+ name = x86;
+ sourceTree = "<group>";
+ };
+ 0CC19C1414B640810095FCAE /* amd64 */ = {
+ isa = PBXGroup;
+ children = (
+ 0C80A06D14B6575700FAA6BA /* nxonce.h */,
+ 0C80A07F14B658C000FAA6BA /* nxonce.c */,
+ );
+ name = amd64;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 0CC19BFC14B63FDC0095FCAE /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0C80A05214B64EC900FAA6BA /* nxstring.h in Headers */,
+ 0C80A05414B64EC900FAA6BA /* nxthread.h in Headers */,
+ 0C80A05914B64F6400FAA6BA /* nxthread.h in Headers */,
+ 0C80A06F14B6575700FAA6BA /* nxonce.h in Headers */,
+ 0C80A08414B65B5000FAA6BA /* nxonce.h in Headers */,
+ 0C80A08A14B65C6800FAA6BA /* nxonce.h in Headers */,
+ 0C5DCD1F14BB67C70077EB3A /* nxpath.h in Headers */,
+ 0C5494D214BB8543005EFB72 /* nxuri.h in Headers */,
+ 0CC9986014C5DBC200484291 /* nxcondition.h in Headers */,
+ B1FE9B9414F5E29000629941 /* nxdata.h in Headers */,
+ 0088410F150657D000625F51 /* nx.h in Headers */,
+ 00884110150657D000625F51 /* nxapi.h in Headers */,
+ 00884111150657D000625F51 /* nxcondition.h in Headers */,
+ 00884112150657D000625F51 /* nxdata.h in Headers */,
+ 00884113150657D000625F51 /* nxfile.h in Headers */,
+ 00884114150657D000625F51 /* nxmutablestring.h in Headers */,
+ 00884115150657D000625F51 /* nxpath.h in Headers */,
+ 00884116150657D000625F51 /* nxsleep.h in Headers */,
+ 00884117150657D000625F51 /* nxstring.h in Headers */,
+ 00884118150657D000625F51 /* nxuri.h in Headers */,
+ 00723DD6150E8CCD006847F2 /* nxfile.h in Headers */,
+ 00723DDA150E8D86006847F2 /* nxsleep.h in Headers */,
+ 00F8A4F4150F0B9700CB104E /* version.h in Headers */,
+ 00B732B4151A625D00A8251C /* nxsemaphore.h in Headers */,
+ 00B732B9151A628A00A8251C /* nxsemaphore.h in Headers */,
+ 00C27E901537245F008D95CD /* precomp.h in Headers */,
+ 00512B97156BD61500115B09 /* nxtime.h in Headers */,
+ 00512B9A156BD62800115B09 /* nxtime.h in Headers */,
+ 00695D1A16E70C1A009CB115 /* nxlog.h in Headers */,
+ 00695D1E16E70D19009CB115 /* nxlog.h in Headers */,
+ 00695D2016E70D2B009CB115 /* NXFileObject.h in Headers */,
+ 0062CA5C170B228300CBFD15 /* MessageLoop.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXLegacyTarget section */
+ 002FD390151981B200857D4F /* nx-cleanup */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(NSBUILD_TOOLS_BIN_DIR)/cleanbuild --xcode-mode --libraries \"$(LIBRARY_PATH)\" \"$(PUBLIC_HEADERS_DIR)\" \"$(DWARF_DSYM_PATH)\" \"$(PROJECT_DERIVED_FILE_DIR)/version.*\"";
+ buildConfigurationList = 002FD391151981B200857D4F /* Build configuration list for PBXLegacyTarget "nx-cleanup" */;
+ buildPhases = (
+ );
+ buildToolPath = /bin/sh;
+ buildWorkingDirectory = "";
+ dependencies = (
+ );
+ name = "nx-cleanup";
+ passBuildSettingsInEnvironment = 1;
+ productName = "nx-cleanup";
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ 0CC19BFD14B63FDC0095FCAE /* nx */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 0CC19C0214B63FDC0095FCAE /* Build configuration list for PBXNativeTarget "nx" */;
+ buildPhases = (
+ 0CC19BFA14B63FDC0095FCAE /* Sources */,
+ 0CC19BFB14B63FDC0095FCAE /* Frameworks */,
+ 00C80456150FCB33005570B4 /* Create Symbolic Links */,
+ 0CC19BFC14B63FDC0095FCAE /* Headers */,
+ 008840FB150656FD00625F51 /* Copy Mac OS X Public Headers */,
+ 008840FD1506572B00625F51 /* Copy Mac OS X amd64 Public Headers */,
+ 008840FC1506572100625F51 /* Copy Mac OS X x86 Public Headers */,
+ 0073ABB5150E69E20027A889 /* Install Public Headers */,
+ 002FD36A15126B7400857D4F /* Install Debug Symbols */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 002FD395151981BC00857D4F /* PBXTargetDependency */,
+ 00B73408151BAE0C00A8251C /* PBXTargetDependency */,
+ );
+ name = nx;
+ productName = nx;
+ productReference = 0CC19BFE14B63FDC0095FCAE /* libnx-1.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0CC19BF514B63FDC0095FCAE /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0500;
+ ORGANIZATIONNAME = "Nullsoft, Inc.";
+ };
+ buildConfigurationList = 0CC19BF814B63FDC0095FCAE /* Build configuration list for PBXProject "nx" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 0CC19BF314B63FDC0095FCAE;
+ productRefGroup = 0CC19BFF14B63FDC0095FCAE /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 0CC19BFD14B63FDC0095FCAE /* nx */,
+ 002FD390151981B200857D4F /* nx-cleanup */,
+ 00B73401151BADB700A8251C /* nx-prepare */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 002FD36A15126B7400857D4F /* Install Debug Symbols */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)",
+ );
+ name = "Install Debug Symbols";
+ outputPaths = (
+ "$(DWARF_DSYM_INSTALL_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "INSTALLTOOL=\"$NSBUILD_TOOLS_BIN_DIR/installtool\"\n$INSTALLTOOL \"$SCRIPT_INPUT_FILE_0/\" \\\n \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 0073ABB5150E69E20027A889 /* Install Public Headers */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ name = "Install Public Headers";
+ outputPaths = (
+ "$(DSTROOT)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "INSTALLTOOL=\"$NSBUILD_TOOLS_BIN_DIR/installtool\"\n$INSTALLTOOL --headers-only \\\n \"$SCRIPT_INPUT_FILE_0/\" \\\n \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 00B73405151BADCC00A8251C /* Generate Version Info */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/VERSION",
+ "$(NSBUILD_TOOLS_SHARE_DIR)/nvgtool/dylib-version.template.h",
+ "$(NSBUILD_TOOLS_SHARE_DIR)/nvgtool/dylib-version.template.c",
+ );
+ name = "Generate Version Info";
+ outputPaths = (
+ "$(PROJECT_DERIVED_FILE_DIR)/version.h",
+ "$(PROJECT_DERIVED_FILE_DIR)/version.c",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "PRODUCT_VERSION=$(cat \"$SCRIPT_INPUT_FILE_0\")\n\nif [ ! -d \"$PROJECT_DERIVED_FILE_DIR\" ]; then\n mkdir -p \"$PROJECT_DERIVED_FILE_DIR\"\nfi\n\nNVGTOOL=\"$NSBUILD_TOOLS_BIN_DIR/nvgtool\"\n$NVGTOOL --product-name \"$PRODUCT_NAME\" \\\n --product-version \"$PRODUCT_VERSION\" \\\n --input-file \"$SCRIPT_INPUT_FILE_1\" \\\n --output-file \"$SCRIPT_OUTPUT_FILE_0\"\n\n$NVGTOOL --product-name \"$PRODUCT_NAME\" \\\n --product-version \"$PRODUCT_VERSION\" \\\n --input-file \"$SCRIPT_INPUT_FILE_2\" \\\n --output-file \"$SCRIPT_OUTPUT_FILE_1\"";
+ showEnvVarsInLog = 0;
+ };
+ 00C80456150FCB33005570B4 /* Create Symbolic Links */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 12;
+ files = (
+ );
+ inputPaths = (
+ "$(TARGET_BUILD_DIR)/$(EXECUTABLE_NAME)",
+ );
+ name = "Create Symbolic Links";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "LIBLN=\"$NSBUILD_TOOLS_BIN_DIR/libln\"\ncd $TARGET_BUILD_DIR; \\\n$LIBLN \"$EXECUTABLE_NAME\"\n\nif [ \"$DEPLOYMENT_LOCATION\" == \"YES\" ]; then\n cd \"$BUILT_PRODUCTS_DIR\"; \\\n $LIBLN \"$EXECUTABLE_NAME\"\nfi";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 0CC19BFA14B63FDC0095FCAE /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0C80A05314B64EC900FAA6BA /* nxthread.c in Sources */,
+ 0C80A08014B658C000FAA6BA /* nxonce.c in Sources */,
+ 0C80A08314B65B5000FAA6BA /* nxonce.c in Sources */,
+ 0C5DCD1914BB5DA40077EB3A /* nxstring.c in Sources */,
+ 0C5DCD1E14BB67C70077EB3A /* nxpath.c in Sources */,
+ 0C5494CF14BB8514005EFB72 /* nxuri.c in Sources */,
+ 0CC9985F14C5DBC200484291 /* nxcondition.c in Sources */,
+ B180783C14F48398000B9733 /* nxsleep.c in Sources */,
+ B1FE9B9314F5E29000629941 /* nxdata.c in Sources */,
+ 00723DD5150E8CCD006847F2 /* nxfile.c in Sources */,
+ 00F8A4F3150F0B9700CB104E /* version.c in Sources */,
+ 00B732B8151A628A00A8251C /* nxsemaphore.c in Sources */,
+ 0074C89E1679008C005E2369 /* NXFileObject.cpp in Sources */,
+ 00695D1816E70C1A009CB115 /* NXFileProgressiveDownloader.cpp in Sources */,
+ 00695D1916E70C1A009CB115 /* nxlog.c in Sources */,
+ 0062CA5B170B228300CBFD15 /* MessageLoop.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 002FD395151981BC00857D4F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 002FD390151981B200857D4F /* nx-cleanup */;
+ targetProxy = 002FD394151981BC00857D4F /* PBXContainerItemProxy */;
+ };
+ 0039B369152A1F3500D96D3E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 002FD390151981B200857D4F /* nx-cleanup */;
+ targetProxy = 0039B368152A1F3500D96D3E /* PBXContainerItemProxy */;
+ };
+ 00B73408151BAE0C00A8251C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00B73401151BADB700A8251C /* nx-prepare */;
+ targetProxy = 00B73407151BAE0C00A8251C /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 002FD392151981B200857D4F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Debug;
+ };
+ 002FD393151981B200857D4F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Release;
+ };
+ 00B73403151BADB700A8251C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ 00B73404151BADB700A8251C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+ 0CC19C0014B63FDC0095FCAE /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DWARF_DSYM_INSTALL_FOLDER_PATH = "$(INSTALL_ROOT)$(INSTALL_PATH_PREFIX)/symbols";
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = dylib;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)-$(DYLIB_CURRENT_VERSION).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib/";
+ INSTALL_PATH_PREFIX = /usr/local;
+ LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)-$(DYLIB_COMPATIBILITY_VERSION).$(EXECUTABLE_EXTENSION)";
+ MACH_O_TYPE = mh_dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ ONLY_ACTIVE_ARCH = YES;
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Debug;
+ };
+ 0CC19C0114B63FDC0095FCAE /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DWARF_DSYM_INSTALL_FOLDER_PATH = "$(INSTALL_ROOT)$(INSTALL_PATH_PREFIX)/symbols";
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = dylib;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)-$(DYLIB_CURRENT_VERSION).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib/";
+ INSTALL_PATH_PREFIX = /usr/local;
+ LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)-$(DYLIB_COMPATIBILITY_VERSION).$(EXECUTABLE_EXTENSION)";
+ MACH_O_TYPE = mh_dylib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ PRODUCT_NAME = "$(PROJECT_NAME)";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = ".. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Release;
+ };
+ 0CC19C0314B63FDC0095FCAE /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks\"",
+ );
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ };
+ name = Debug;
+ };
+ 0CC19C0414B63FDC0095FCAE /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks\"",
+ );
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 002FD391151981B200857D4F /* Build configuration list for PBXLegacyTarget "nx-cleanup" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 002FD392151981B200857D4F /* Debug */,
+ 002FD393151981B200857D4F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 00B73402151BADB700A8251C /* Build configuration list for PBXAggregateTarget "nx-prepare" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00B73403151BADB700A8251C /* Debug */,
+ 00B73404151BADB700A8251C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 0CC19BF814B63FDC0095FCAE /* Build configuration list for PBXProject "nx" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0CC19C0014B63FDC0095FCAE /* Debug */,
+ 0CC19C0114B63FDC0095FCAE /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 0CC19C0214B63FDC0095FCAE /* Build configuration list for PBXNativeTarget "nx" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0CC19C0314B63FDC0095FCAE /* Debug */,
+ 0CC19C0414B63FDC0095FCAE /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0CC19BF514B63FDC0095FCAE /* Project object */;
+}
diff --git a/Src/replicant/nx/nx.xcodeproj/xcshareddata/xcschemes/nx.xcscheme b/Src/replicant/nx/nx.xcodeproj/xcshareddata/xcschemes/nx.xcscheme
new file mode 100644
index 000000000..98d012c47
--- /dev/null
+++ b/Src/replicant/nx/nx.xcodeproj/xcshareddata/xcschemes/nx.xcscheme
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0500"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "0CC19BFD14B63FDC0095FCAE"
+ BuildableName = "libnx-1.dylib"
+ BlueprintName = "nx"
+ ReferencedContainer = "container:nx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Src/replicant/nx/nx.xcworkspace/contents.xcworkspacedata b/Src/replicant/nx/nx.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..d2ee22efe
--- /dev/null
+++ b/Src/replicant/nx/nx.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:../foundation/foundation.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:../nu/nu.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:../jnetlib/jnetlib.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:nx.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/Src/replicant/nx/nx.xcworkspace/xcshareddata/xcschemes/nx-complete.xcscheme b/Src/replicant/nx/nx.xcworkspace/xcshareddata/xcschemes/nx-complete.xcscheme
new file mode 100644
index 000000000..55220a780
--- /dev/null
+++ b/Src/replicant/nx/nx.xcworkspace/xcshareddata/xcschemes/nx-complete.xcscheme
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0440"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "0088424E150716D200625F51"
+ BuildableName = "foundation"
+ BlueprintName = "foundation"
+ ReferencedContainer = "container:../foundation/foundation.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "0039B398152A384100D96D3E"
+ BuildableName = "libnu.a"
+ BlueprintName = "nu"
+ ReferencedContainer = "container:../nu/nu.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "0CC19BFD14B63FDC0095FCAE"
+ BuildableName = "libnx-1.dylib"
+ BlueprintName = "nx"
+ ReferencedContainer = "container:nx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Src/replicant/nx/nxapi.h b/Src/replicant/nx/nxapi.h
new file mode 100644
index 000000000..de5433aef
--- /dev/null
+++ b/Src/replicant/nx/nxapi.h
@@ -0,0 +1,12 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxapi.h"
+#elif defined(__linux__)
+#include "linux/nxapi.h"
+#elif defined(_WIN32)
+#include "win/nxapi.h"
+#elif defined(__APPLE__)
+#include "osx/nxapi.h"
+#else
+#error port me!
+#endif
diff --git a/Src/replicant/nx/nxcondition.h b/Src/replicant/nx/nxcondition.h
new file mode 100644
index 000000000..42cfead5d
--- /dev/null
+++ b/Src/replicant/nx/nxcondition.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxcondition.h"
+#elif defined(_WIN32)
+#include "win/nxcondition.h"
+#elif defined(__APPLE__)
+#include "osx/nxcondition.h"
+#elif defined(__linux__)
+#include "linux/nxcondition.h"
+#endif
diff --git a/Src/replicant/nx/nxdata.h b/Src/replicant/nx/nxdata.h
new file mode 100644
index 000000000..721601a91
--- /dev/null
+++ b/Src/replicant/nx/nxdata.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxdata.h"
+#elif defined(__linux__)
+#include "linux/nxdata.h"
+#elif defined(_WIN32)
+#include "win/nxdata.h"
+#elif defined(__APPLE__)
+#include "osx/nxdata.h"
+#endif
diff --git a/Src/replicant/nx/nxfile.h b/Src/replicant/nx/nxfile.h
new file mode 100644
index 000000000..4f778acf1
--- /dev/null
+++ b/Src/replicant/nx/nxfile.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxfile.h"
+#elif defined(__linux__)
+#include "linux/nxfile.h"
+#elif defined(_WIN32)
+#include "win/nxfile.h"
+#elif defined(__APPLE__)
+#include "osx/nxfile.h"
+#endif
diff --git a/Src/replicant/nx/nximage.h b/Src/replicant/nx/nximage.h
new file mode 100644
index 000000000..f6a1cf4f8
--- /dev/null
+++ b/Src/replicant/nx/nximage.h
@@ -0,0 +1,4 @@
+#pragma once
+
+/* benski> this is a work in a progress and might go away at any point */
+#include "win/nximage.h"
diff --git a/Src/replicant/nx/nxlog.h b/Src/replicant/nx/nxlog.h
new file mode 100644
index 000000000..57a0a0e6e
--- /dev/null
+++ b/Src/replicant/nx/nxlog.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxlog.h"
+#elif defined(__linux__)
+#include "linux/nxlog.h"
+#elif defined(_WIN32)
+#include "win/nxlog.h"
+#elif defined(__APPLE__)
+#include "osx/nxlog.h"
+#endif
diff --git a/Src/replicant/nx/nxmutablestring.h b/Src/replicant/nx/nxmutablestring.h
new file mode 100644
index 000000000..100af7e92
--- /dev/null
+++ b/Src/replicant/nx/nxmutablestring.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxmutablestring.h"
+#elif defined(__linux__)
+#include "linux/nxmutablestring.h"
+#elif defined(_WIN32)
+#include "win/nxmutablestring.h"
+#elif defined(__APPLE_)
+#include "osx/nxmutablestring.h"
+#endif
diff --git a/Src/replicant/nx/nxonce.h b/Src/replicant/nx/nxonce.h
new file mode 100644
index 000000000..4a5dc37d0
--- /dev/null
+++ b/Src/replicant/nx/nxonce.h
@@ -0,0 +1,14 @@
+#pragma once
+#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
+#include "android-armv7/nxonce.h"
+#elif defined(__ANDROID__)
+#include "android-armv5/nxonce.h"
+#elif defined(_WIN32)
+#include "win/nxonce.h"
+#elif defined(__APPLE__) && defined(__amd64__)
+#include "osx-amd64/nxonce.h"
+#elif defined(__APPLE__) && defined(__i386__)
+#include "osx-x86/nxonce.h"
+#elif defined(__linux__)
+#include "linux/nxonce.h"
+#endif
diff --git a/Src/replicant/nx/nxpath.h b/Src/replicant/nx/nxpath.h
new file mode 100644
index 000000000..61080cae1
--- /dev/null
+++ b/Src/replicant/nx/nxpath.h
@@ -0,0 +1,8 @@
+#pragma once
+#if defined(__linux__)
+#include "linux/nxpath.h"
+#elif defined(_WIN32)
+#include "win/nxpath.h"
+#elif defined(__APPLE__)
+#include "osx/nxpath.h"
+#endif
diff --git a/Src/replicant/nx/nxsemaphore.h b/Src/replicant/nx/nxsemaphore.h
new file mode 100644
index 000000000..1baa0d9fe
--- /dev/null
+++ b/Src/replicant/nx/nxsemaphore.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxsemaphore.h"
+#elif defined(_WIN32)
+#include "win/nxsemaphore.h"
+#elif defined(__APPLE__)
+#include "osx/nxsemaphore.h"
+#elif defined(__linux__)
+#include "linux/nxsemaphore.h"
+#endif
diff --git a/Src/replicant/nx/nxsleep.h b/Src/replicant/nx/nxsleep.h
new file mode 100644
index 000000000..2e404e071
--- /dev/null
+++ b/Src/replicant/nx/nxsleep.h
@@ -0,0 +1,8 @@
+#pragma once
+#if defined(__linux__)
+#include "linux/nxsleep.h"
+#elif defined(_WIN32)
+#include "win/nxsleep.h"
+#elif defined(__APPLE__)
+#include "osx/nxsleep.h"
+#endif
diff --git a/Src/replicant/nx/nxstring.h b/Src/replicant/nx/nxstring.h
new file mode 100644
index 000000000..b4da51e20
--- /dev/null
+++ b/Src/replicant/nx/nxstring.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxstring.h"
+#elif defined(__linux__)
+#include "linux/nxstring.h"
+#elif defined(_WIN32)
+#include "win/nxstring.h"
+#elif defined(__APPLE__)
+#include "osx/nxstring.h"
+#endif
diff --git a/Src/replicant/nx/nxthread.h b/Src/replicant/nx/nxthread.h
new file mode 100644
index 000000000..21e751102
--- /dev/null
+++ b/Src/replicant/nx/nxthread.h
@@ -0,0 +1,8 @@
+#pragma once
+#if defined(__linux__)
+#include "linux/nxthread.h"
+#elif defined(_WIN32)
+#include "win/nxthread.h"
+#elif defined(__APPLE__)
+#include "osx/nxthread.h"
+#endif
diff --git a/Src/replicant/nx/nxtime.h b/Src/replicant/nx/nxtime.h
new file mode 100644
index 000000000..cbd629b10
--- /dev/null
+++ b/Src/replicant/nx/nxtime.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxtime.h"
+#elif defined(__linux__)
+#include "linux/nxtime.h"
+#elif defined(_WIN32)
+#include "win/nxtime.h"
+#elif defined(__APPLE__)
+#include "osx/nxtime.h"
+#endif
diff --git a/Src/replicant/nx/nxuri.h b/Src/replicant/nx/nxuri.h
new file mode 100644
index 000000000..85decb56b
--- /dev/null
+++ b/Src/replicant/nx/nxuri.h
@@ -0,0 +1,10 @@
+#pragma once
+#if defined(__ANDROID__)
+#include "android/nxuri.h"
+#elif defined(__linux__)
+#include "linux/nxuri.h"
+#elif defined(_WIN32)
+#include "win/nxuri.h"
+#elif defined(__APPLE__)
+#include "osx/nxuri.h"
+#endif
diff --git a/Src/replicant/nx/resource.h b/Src/replicant/nx/resource.h
new file mode 100644
index 000000000..31853f039
--- /dev/null
+++ b/Src/replicant/nx/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by nx.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/replicant/nx/version.rc2 b/Src/replicant/nx/version.rc2
new file mode 100644
index 000000000..becc4bd20
--- /dev/null
+++ b/Src/replicant/nx/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION WINAMP_PRODUCTVER
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Support Library"
+ VALUE "FileVersion", STR_WINAMP_PRODUCTVER
+ VALUE "InternalName", "nxlite.dll"
+ VALUE "LegalCopyright", "Copyright © 2012-2014 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "nxlite.dll"
+ VALUE "ProductName", "Winamp Shared Code Library"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/replicant/nx/win/NXFileObject.cpp b/Src/replicant/nx/win/NXFileObject.cpp
new file mode 100644
index 000000000..8f1255dbf
--- /dev/null
+++ b/Src/replicant/nx/win/NXFileObject.cpp
@@ -0,0 +1,525 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include "NXFileObject.h"
+
+#include "nx/nxfile.h"
+#include "nu/nodelist.h"
+#include "foundation/atomics.h"
+#include "nu/nodelist.h"
+#include <errno.h>
+#include <new>
+
+/* Windows implementation
+note: for now, we're using FILE. We will eventually replace with better buffering
+
+TODO: deal with files opened in "append" mode */
+
+NXFileObject::NXFileObject()
+{
+ nodelist_init(&region_stack);
+ region.start = 0;
+ region.end = 0xFFFFFFFFFFFFFFFFULL;
+ position=0;
+ reference_count=1;
+ uri=0;
+ memset(&file_stats, 0, sizeof(file_stats));
+}
+
+NXFileObject::~NXFileObject()
+{
+ NXURIRelease(uri);
+ NXFileRegion *region = (NXFileRegion *)region_stack.head;
+ while (region)
+ {
+ NXFileRegion *next = (NXFileRegion *)region->Next;
+ free(region);
+ region = next;
+ }
+}
+
+ns_error_t NXFileObject::Initialize(nx_uri_t _uri)
+{
+ uri = NXURIRetain(_uri);
+ return NErr_Success;
+}
+
+size_t NXFileObject::Retain()
+{
+ return nx_atomic_inc(&reference_count);
+}
+
+size_t NXFileObject::Release()
+{
+ if (!reference_count)
+ {
+ return reference_count;
+ }
+ size_t r = nx_atomic_dec_release(&reference_count);
+ if (!r)
+ {
+ delete(this);
+ }
+ return r;
+}
+
+ns_error_t NXFileObject::LockRegion(uint64_t start, uint64_t end)
+{
+ // save the old region data
+ NXFileRegion *old_region = (NXFileRegion *)calloc(1, sizeof(NXFileRegion));
+ if (!old_region)
+ {
+ return NErr_OutOfMemory;
+ }
+ old_region->start = region.start;
+ old_region->end = region.end;
+ nodelist_push_front(&region_stack, old_region);
+
+ // if we're already locked, Lock within our current region.
+ // The weird way the logic is done prevents overflow
+ if (start > region.end - region.start)
+ {
+ start = region.end;
+ }
+ else
+ {
+ start = region.start + start;
+ }
+
+ if (end > region.end - region.start)
+ {
+ end = region.end;
+ }
+ else
+ {
+ end = region.start + end;
+ }
+
+ region.start = start;
+ region.end = end;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject::UnlockRegion()
+{
+ NXFileRegion *new_region = (NXFileRegion *)nodelist_pop_front(&region_stack);
+ if (new_region)
+ {
+ region.start = new_region->start;
+ region.end = new_region->end;
+ free(new_region);
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_NoAction;
+ }
+}
+
+ns_error_t NXFileObject::Stat(nx_file_stat_t out_stats)
+{
+ *out_stats = file_stats;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject::Length(uint64_t *length)
+{
+ *length = region.end - region.start;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject::EndOfFile()
+{
+ if (position >= region.end)
+ {
+ return NErr_True;
+ }
+ else
+ {
+ return NErr_False;
+ }
+}
+/* ----------------------------------------- */
+
+class NXFileObject_FILE : public NXFileObject
+{
+public:
+ NXFileObject_FILE();
+ ~NXFileObject_FILE();
+ ns_error_t Initialize(nx_uri_t uri, FILE *f, bool writable);
+
+private:
+ FILE *f;
+ bool writable;
+
+ ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read);
+ ns_error_t Write(const void *buffer, size_t bytes);
+ ns_error_t Seek(uint64_t position);
+ ns_error_t Tell(uint64_t *position);
+ ns_error_t PeekByte(uint8_t *byte);
+ ns_error_t Sync();
+ ns_error_t Truncate();
+};
+
+NXFileObject_FILE::NXFileObject_FILE()
+{
+ f = 0;
+ writable = false;
+}
+
+NXFileObject_FILE::~NXFileObject_FILE()
+{
+ if (f)
+ {
+ fclose(f);
+ }
+}
+
+ns_error_t NXFileObject_FILE::Initialize(nx_uri_t uri, FILE *_f, bool _writable)
+{
+ writable = _writable;
+
+ ns_error_t ret = NXFileObject::Initialize(uri);
+ if (ret != NErr_Success)
+ {
+ return ret;
+ }
+
+ ret = NXFile_statFILE(_f, &file_stats);
+ if (ret != NErr_Success)
+ {
+ return ret;
+ }
+
+ region.end = file_stats.file_size;
+
+ f = _f;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_FILE::Read(void *buffer, size_t bytes_requested, size_t *bytes_read)
+{
+ // if it's an "empty" read, we need to determine whether or not we're at the end of the file
+ if (bytes_requested == 0)
+ {
+ if (region.end == position || feof(f))
+ {
+ return NErr_EndOfFile;
+ }
+ else
+ {
+ return NErr_Success;
+ }
+ }
+
+ // don't read into any data after the locked region
+ if ((uint64_t)bytes_requested > region.end - position)
+ {
+ bytes_requested = (size_t)(region.end - position);
+ }
+
+ if (bytes_requested == 0)
+ {
+ return NErr_EndOfFile;
+ }
+
+ if (buffer == 0)
+ {
+ uint64_t old_position=position;
+ Seek(position+bytes_requested);
+ if (bytes_read)
+ {
+ *bytes_read = (size_t)(position - old_position);
+ }
+ return NErr_Success;
+ }
+ else
+ {
+ size_t results = fread(buffer, 1, bytes_requested, f);
+ if (results == 0)
+ {
+ if (feof(f))
+ {
+ return NErr_EndOfFile;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+ }
+ if (bytes_read)
+ {
+ *bytes_read = results;
+ }
+ position+=results;
+ return NErr_Success;
+ }
+}
+
+ns_error_t NXFileObject_FILE::Write(const void *buffer, size_t bytes)
+{
+ // TODO: review this in relation to locked regions
+ size_t results = fwrite(buffer, 1, bytes, f);
+ if (results == 0)
+ {
+ return NErr_Error;
+ }
+
+ position += results;
+ if (region.end < position)
+ {
+ region.end = position;
+ }
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_FILE::PeekByte(uint8_t *byte)
+{
+ if (position == region.end)
+ {
+ return NErr_EndOfFile;
+ }
+
+ int read_byte = fgetc(f);
+ if (read_byte != EOF)
+ {
+ ungetc(read_byte, f);
+ }
+ else
+ {
+ return NErr_EndOfFile;
+ }
+
+ *byte = (uint8_t)read_byte;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_FILE::Seek(uint64_t new_position)
+{
+ if (!writable)
+ {
+ // doing it this way will prevent integer overflow
+ if (new_position > (region.end - region.start))
+ {
+ new_position = region.end - region.start;
+ }
+ }
+
+ if (_fseeki64(f, region.start+new_position, SEEK_SET) == 0)
+ {
+ position = region.start+new_position;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+}
+
+ns_error_t NXFileObject_FILE::Tell(uint64_t *out_position)
+{
+ *out_position = position - region.start;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_FILE::Sync()
+{
+ fflush(f);
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_FILE::Truncate()
+{
+ int fd = _fileno(f);
+ _chsize_s(fd, position);
+ return NErr_Success;
+}
+
+/* ----------------------------------------- */
+ns_error_t NXFileOpenFile(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags)
+{
+ FILE *f = NXFile_fopen(filename, flags);
+ if (!f)
+ {
+ if (errno == ENOENT)
+ {
+ return NErr_FileNotFound;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+ }
+
+ NXFileObject_FILE *file_object = new (std::nothrow) NXFileObject_FILE;
+ if (!file_object)
+ {
+ fclose(f);
+ return NErr_OutOfMemory;
+ }
+
+ ns_error_t ret = file_object->Initialize(filename, f, !!(flags & nx_file_FILE_writable_mask));
+ if (ret != NErr_Success)
+ {
+ fclose(f);
+ delete file_object;
+ return ret;
+ }
+
+ *out_file = (nx_file_t)file_object;
+ return NErr_Success;
+}
+
+nx_file_t NXFileRetain(nx_file_t _f)
+{
+ if (!_f)
+ {
+ return 0;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ f->Retain();
+ return _f;
+}
+
+void NXFileRelease(nx_file_t _f)
+{
+ if (_f)
+ {
+ NXFileObject *f = (NXFileObject *)_f;
+ f->Release();
+ }
+}
+
+ns_error_t NXFileRead(nx_file_t _f, void *buffer, size_t bytes_requested, size_t *bytes_read)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Read(buffer, bytes_requested, bytes_read);
+}
+
+ns_error_t NXFileSeek(nx_file_t _f, uint64_t position)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Seek(position);
+}
+
+ns_error_t NXFileTell(nx_file_t _f, uint64_t *position)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Tell(position);
+}
+
+ns_error_t NXFileLockRegion(nx_file_t _f, uint64_t start_position, uint64_t end_position)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->LockRegion(start_position, end_position);
+}
+
+ns_error_t NXFileUnlockRegion(nx_file_t _f)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->UnlockRegion();
+}
+
+ns_error_t NXFileStat(nx_file_t _f, nx_file_stat_t file_stats)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Stat(file_stats);
+}
+
+ns_error_t NXFileLength(nx_file_t _f, uint64_t *length)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Length(length);
+}
+
+ns_error_t NXFileEndOfFile(nx_file_t _f)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->EndOfFile();
+}
+
+ns_error_t NXFilePeekByte(nx_file_t _f, uint8_t *byte)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->PeekByte(byte);
+}
+
+ns_error_t NXFileWrite(nx_file_t _f, const void *buffer, size_t bytes)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Write(buffer, bytes);
+}
+
+ns_error_t NXFileSync(nx_file_t _f)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Sync();
+}
+
+ns_error_t NXFileTruncate(nx_file_t _f)
+{
+ if (!_f)
+ {
+ return NErr_BadParameter;
+ }
+
+ NXFileObject *f = (NXFileObject *)_f;
+ return f->Truncate();
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/NXFileObject.h b/Src/replicant/nx/win/NXFileObject.h
new file mode 100644
index 000000000..46683bfa7
--- /dev/null
+++ b/Src/replicant/nx/win/NXFileObject.h
@@ -0,0 +1,45 @@
+#pragma once
+#include "foundation/types.h"
+#include "foundation/error.h"
+#include "nx/nxuri.h"
+#include "nx/nxfile.h"
+#include "nu/nodelist.h"
+
+struct NXFileRegion : public queue_node_t
+{
+ uint64_t start;
+ uint64_t end;
+};
+
+class NXFileObject
+{
+public:
+ size_t Retain();
+ size_t Release();
+ ns_error_t LockRegion(uint64_t start, uint64_t end);
+ ns_error_t UnlockRegion();
+ ns_error_t Stat(nx_file_stat_t out_stats);
+ ns_error_t Length(uint64_t *length);
+
+ virtual ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read)=0;
+ virtual ns_error_t Write(const void *buffer, size_t bytes)=0;
+ virtual ns_error_t PeekByte(uint8_t *byte)=0;
+ virtual ns_error_t Seek(uint64_t position)=0;
+ virtual ns_error_t Tell(uint64_t *position)=0;
+ virtual ns_error_t Sync()=0;
+ virtual ns_error_t Truncate()=0;
+
+ virtual ns_error_t EndOfFile();
+
+protected:
+ NXFileObject();
+ virtual ~NXFileObject();
+
+ nx_file_stat_s file_stats;
+ uint64_t position; /* note: this represents absolute position, _not_ position within the region */
+ ns_error_t Initialize(nx_uri_t uri);
+ NXFileRegion region;
+ nodelist_s region_stack;
+ nx_uri_t uri;
+ volatile size_t reference_count;
+}; \ No newline at end of file
diff --git a/Src/replicant/nx/win/NXFileProgressiveDownloader.cpp b/Src/replicant/nx/win/NXFileProgressiveDownloader.cpp
new file mode 100644
index 000000000..a8452c726
--- /dev/null
+++ b/Src/replicant/nx/win/NXFileProgressiveDownloader.cpp
@@ -0,0 +1,749 @@
+#include "NXFileObject.h"
+#include "nu/ProgressTracker.h"
+#include "nx/nxthread.h"
+#include "nx/nxsleep.h"
+#include "jnetlib/jnetlib.h"
+#include "../nswasabi/AutoCharNX.h"
+#include "nswasabi/ReferenceCounted.h"
+#include "nu/MessageLoop.h"
+#include <time.h>
+#include <new>
+#include "../../../WAT/WAT.h"
+
+/* TODO: benski> test this with a server that does not return content-length. I bet we could get it to work */
+
+/* TODO: benski> on windows, we can use a single CreateFile HANDLE for both reading and writing
+ and use ReadFile(..., &overlapped) to maintain two separate file pointers
+ this should improve performance as they will share the same cache
+ _might_ have to use async I/O to get it to work (but use it synchronously by waiting on the handle after making the call
+ */
+
+#define HTTP_BUFFER_SIZE 65536
+
+class NXFileObject_ProgressiveDownloader;
+
+enum
+{
+ MESSAGE_KILL,
+ MESSAGE_SEEK,
+ MESSAGE_SIZE,
+ MESSAGE_ERROR,
+ MESSAGE_CLOSED,
+ MESSAGE_CONNECTED,
+};
+
+char MessageString[6][10] =
+{
+ "Kill",
+ "Seek",
+ "Size",
+ "Error",
+ "Closed",
+ "Connected"
+};
+
+
+struct seek_message_t : public nu::message_node_t
+{
+ uint64_t start;
+ uint64_t end;
+};
+
+struct size_message_t : public nu::message_node_t
+{
+ uint64_t size;
+};
+
+struct error_message_t : public nu::message_node_t
+{
+ int error_code;
+};
+
+/* This class represents the thread that's actually downloading the content from the server */
+class ProgressiveDownload
+{
+public:
+ ProgressiveDownload(ProgressTracker &progress_tracker, NXFileObject_ProgressiveDownloader &parent);
+ ~ProgressiveDownload();
+ ns_error_t Initialize(nx_uri_t uri, jnl_http_t http, const char *user_agent, nx_uri_t temp_uri);
+
+ void Seek(uint64_t start, uint64_t end);
+ void Close();
+private:
+ /* These functions are called on the local thread */
+ /* These functions run on the download thread */
+ static nx_thread_return_t NXTHREADCALL _ProgressiveThread(nx_thread_parameter_t param) { return ((ProgressiveDownload *)param)->ProgressiveThread(); }
+ nx_thread_return_t NXTHREADCALL ProgressiveThread();
+ int Connect();
+ void Internal_Write(const void *data, size_t data_len);
+ int Wait(int milliseconds);
+ ns_error_t SetupConnection(uint64_t start_position, uint64_t end_position);
+ int DoRead(void *buffer, size_t bufferlen);
+ void ProcessMessage(nu::message_node_t *message);
+private:
+ ProgressTracker &progress_tracker;
+ NXFileObject_ProgressiveDownloader &parent;
+
+ nx_uri_t temp_filename, url;
+ FILE *progressive_file_write;
+ jnl_http_t http;
+ char *user_agent;
+ nx_thread_t download_thread;
+ nu::MessageLoop message_loop;
+ uint64_t file_size;
+ int killswitch;
+};
+
+class NXFileObject_ProgressiveDownloader: public NXFileObject
+{
+public:
+ NXFileObject_ProgressiveDownloader();
+ ~NXFileObject_ProgressiveDownloader();
+ ns_error_t Initialize(nx_uri_t uri, jnl_http_t http, const char *user_agent);
+
+ bool Available(uint64_t size, uint64_t *available);
+
+ /* API used by ProgressiveDownload */
+ void OnFileSize(uint64_t filesize);
+ void OnConnected();
+ void OnError(int error_code);
+ void OnClosed();
+private:
+ /* NXFileObject implementation */
+ ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read);
+ ns_error_t Write(const void *buffer, size_t bytes);
+ ns_error_t Seek(uint64_t position);
+ ns_error_t Tell(uint64_t *position);
+ ns_error_t PeekByte(uint8_t *byte);
+ ns_error_t Sync();
+ ns_error_t Truncate();
+
+ bool WaitForRead(uint64_t size);
+ void ProcessMessage(nu::message_node_t *message);
+ void Wait(unsigned int milliseconds);
+
+ ProgressiveDownload download;
+ ProgressTracker progress_tracker;
+ FILE *progressive_file_read;
+ bool end_of_file;
+ bool connected;
+ int error_code;
+ nu::MessageLoop message_loop;
+ bool closed;
+ bool need_seek; // if set to true, we need to fseek(position)
+};
+
+ProgressiveDownload::ProgressiveDownload(ProgressTracker &progress_tracker, NXFileObject_ProgressiveDownloader &parent) : progress_tracker(progress_tracker), parent(parent)
+{
+ killswitch=0;
+ url=0;
+ temp_filename=0;
+ progressive_file_write=0;
+ http=0;
+ user_agent=0;
+ download_thread=0;
+ file_size=0;
+}
+
+ProgressiveDownload::~ProgressiveDownload()
+{
+ if (download_thread)
+ {
+ Close();
+ NXThreadJoin(download_thread, 0);
+ }
+
+ // TODO: flush messages
+ if (progressive_file_write)
+ fclose(progressive_file_write);
+ NXURIRelease(temp_filename);
+ NXURIRelease(url);
+ if (http)
+ jnl_http_release(http);
+ free(user_agent);
+}
+
+void ProgressiveDownload::Close()
+{
+ nu::message_node_t *message = message_loop.AllocateMessage();
+ message->message = MESSAGE_KILL;
+ message_loop.PostMessage(message);
+}
+
+void ProgressiveDownload::Seek(uint64_t start, uint64_t end)
+{
+ seek_message_t *message = (seek_message_t *)message_loop.AllocateMessage();
+ message->message = MESSAGE_SEEK;
+ message->start = start;
+ message->end = end;
+ message_loop.PostMessage(message);
+}
+
+ns_error_t ProgressiveDownload::Initialize(nx_uri_t url, jnl_http_t http, const char *user_agent, nx_uri_t temp_filename)
+{
+ this->url = NXURIRetain(url);
+ this->temp_filename = NXURIRetain(temp_filename);
+ if (user_agent)
+ this->user_agent = strdup(user_agent);
+ this->http = jnl_http_retain(http);
+ progressive_file_write = NXFile_fopen(temp_filename, nx_file_FILE_readwrite_binary);
+ if (progressive_file_write == 0)
+ return NErr_FailedCreate;
+
+ return NXThreadCreate(&download_thread, _ProgressiveThread, this);
+}
+
+void ProgressiveDownload::ProcessMessage(nu::message_node_t *message)
+{
+ switch(message->message)
+ {
+ case MESSAGE_KILL:
+ killswitch=1;
+ break;
+ case MESSAGE_SEEK:
+ {
+ seek_message_t *seek_message = (seek_message_t *)message;
+
+ char buffer[HTTP_BUFFER_SIZE] = {0};
+
+ /* empty out the jnetlib buffer. that might let us be able to avoid this seek */
+ DoRead(buffer, sizeof(buffer));
+
+ uint64_t new_start, new_end;
+ if (!progress_tracker.Valid(seek_message->start, seek_message->end) /* double check that we actually need to seek */
+ && !progress_tracker.Seek(seek_message->start, seek_message->end, &new_start, &new_end))
+ {
+ int ret = SetupConnection(new_start, new_end);
+ if (ret == NErr_Success)
+ ret = Connect();
+ if (ret != NErr_Success)
+ {
+ parent.OnError(ret);
+ killswitch=1;
+ break;
+ }
+
+ _fseeki64(progressive_file_write, new_start, SEEK_SET);
+ }
+ else
+ parent.OnConnected();
+ }
+ break;
+ }
+
+ message_loop.FreeMessage(message);
+}
+
+int ProgressiveDownload::Wait(int milliseconds)
+{
+ for (;;)
+ {
+ if (killswitch)
+ return 1;
+
+ nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
+ if (message)
+ ProcessMessage(message);
+ else
+ break;
+ }
+
+ nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
+ if (message)
+ ProcessMessage(message);
+
+ return killswitch;
+}
+
+ns_error_t ProgressiveDownload::SetupConnection(uint64_t start_position, uint64_t end_position)
+{
+ if (!http)
+ http = jnl_http_create(HTTP_BUFFER_SIZE, 0);
+
+ if (!http)
+ return NErr_FailedCreate;
+
+ jnl_http_reset_headers(http);
+ if (user_agent)
+ jnl_http_addheadervalue(http, "User-Agent", user_agent);
+
+ if (start_position && start_position != (uint64_t)-1)
+ {
+ if (end_position == (uint64_t)-1)
+ {
+ char temp[128] = {0};
+ sprintf(temp, "Range: bytes=%llu-", start_position);
+ jnl_http_addheader(http, temp);
+ }
+ else
+ {
+ char temp[128] = {0};
+ sprintf(temp, "Range: bytes=%llu-%llu", start_position, end_position);
+ jnl_http_addheader(http, temp);
+ }
+ }
+
+ jnl_http_addheader(http, "Connection: Close"); // TODO: change if we ever want a persistent connection and downloading in chunks
+ jnl_http_connect(http, AutoCharUTF8(url), 1, "GET");
+
+ return NErr_Success;
+}
+
+int ProgressiveDownload::Connect()
+{
+ // TODO: configurable timeout
+ /* wait for connection */
+#ifdef _DEBUG
+ const int timeout = 15000;
+#else
+ const int timeout = 15;
+#endif
+ time_t start_time = time(0);
+
+ int http_status = jnl_http_get_status(http);
+ while (http_status == HTTPGET_STATUS_CONNECTING || http_status == HTTPGET_STATUS_READING_HEADERS)
+ {
+ if (Wait(55) != 0)
+ return NErr_Interrupted;
+
+ int ret = jnl_http_run(http);
+ if (ret == HTTPGET_RUN_ERROR)
+ return NErr_ConnectionFailed;
+ if (start_time + timeout < time(0))
+ return NErr_TimedOut;
+
+ http_status = jnl_http_get_status(http);
+ }
+
+ if (http_status == HTTPGET_STATUS_ERROR)
+ {
+ switch(jnl_http_getreplycode(http))
+ {
+ case 400:
+ return NErr_BadRequest;
+ case 401:
+ // TODO: deal with this specially
+ return NErr_Unauthorized;
+ case 403:
+ // TODO: deal with this specially?
+ return NErr_Forbidden;
+ case 404:
+ return NErr_NotFound;
+ case 405:
+ return NErr_BadMethod;
+ case 406:
+ return NErr_NotAcceptable;
+ case 407:
+ // TODO: deal with this specially
+ return NErr_ProxyAuthenticationRequired;
+ case 408:
+ return NErr_RequestTimeout;
+ case 409:
+ return NErr_Conflict;
+ case 410:
+ return NErr_Gone;
+ case 500:
+ return NErr_InternalServerError;
+ case 503:
+ return NErr_ServiceUnavailable;
+ default:
+ return NErr_ConnectionFailed;
+ }
+ }
+ else
+ {
+ if (!file_size)
+ {
+ // TODO: check range header for actual size
+ file_size = jnl_http_content_length(http);
+ parent.OnFileSize(file_size);
+ }
+ parent.OnConnected();
+ return NErr_Success;
+ }
+}
+
+void ProgressiveDownload::Internal_Write(const void *data, size_t data_len)
+{
+ size_t bytes_written = fwrite(data, 1, data_len, progressive_file_write);
+ fflush(progressive_file_write);
+ progress_tracker.Write(bytes_written);
+}
+
+int ProgressiveDownload::DoRead(void *buffer, size_t bufferlen)
+{
+ int ret = jnl_http_run(http);
+ size_t bytes_received;
+ do
+ {
+ ret = jnl_http_run(http);
+ bytes_received = jnl_http_get_bytes(http, buffer, bufferlen);
+ if (bytes_received)
+ {
+ Internal_Write(buffer, bytes_received);
+ }
+ /* TODO: benski> should we limit the number of times through this loop?
+ I'm worried that if data comes in fast enough we might get stuck in this for a long time */
+ } while (bytes_received == bufferlen);
+ return ret;
+}
+
+nx_thread_return_t ProgressiveDownload::ProgressiveThread()
+{
+ ns_error_t ret;
+
+ if (!http)
+ {
+ ret = SetupConnection(0, (uint64_t)-1);
+ if (ret != NErr_Success)
+ {
+ parent.OnError(ret);
+ parent.OnClosed();
+ return 0;
+ }
+ }
+
+
+ ret = Connect();
+ if (ret != NErr_Success)
+ {
+ parent.OnError(ret);
+ }
+ else
+ {
+ for (;;)
+ {
+ if (Wait(10) == 1)
+ break; // killed!
+
+ char buffer[HTTP_BUFFER_SIZE] = {0};
+ int ret = DoRead(buffer, sizeof(buffer));
+ if (ret == -1)
+ break;
+ else if (ret == HTTPGET_RUN_CONNECTION_CLOSED)
+ {
+ if (jnl_http_bytes_available(http) == 0)
+ {
+ if (progress_tracker.Valid(0, file_size))
+ {
+ // file is completely downloaded. let's gtfo
+ fclose(progressive_file_write);
+ progressive_file_write=0;
+ break;
+ }
+
+ // if we're not completely full then we need to sit around for a potential MESSAGE_SEEK
+ //while (Wait(100) == 0)
+ {
+ // nop
+ }
+ }
+ }
+ }
+ }
+
+ parent.OnClosed();
+ return 0;
+}
+
+ /* ------------------ */
+NXFileObject_ProgressiveDownloader::NXFileObject_ProgressiveDownloader() : download(progress_tracker, *this)
+{
+ progressive_file_read=0;
+ end_of_file=false;
+ connected=false;
+ error_code=NErr_Success;
+ closed = false;
+ need_seek=false;
+ position=0;
+}
+
+
+
+NXFileObject_ProgressiveDownloader::~NXFileObject_ProgressiveDownloader()
+{
+ download.Close();
+ while (!closed)
+ Wait(10);
+ if (progressive_file_read)
+ fclose(progressive_file_read);
+}
+
+void NXFileObject_ProgressiveDownloader::OnConnected()
+{
+ nu::message_node_t *message = message_loop.AllocateMessage();
+ message->message = MESSAGE_CONNECTED;
+ message_loop.PostMessage(message);
+}
+
+void NXFileObject_ProgressiveDownloader::OnError(int error_code)
+{
+ error_message_t *message = (error_message_t *)message_loop.AllocateMessage();
+ message->message = MESSAGE_ERROR;
+ message->error_code = error_code;
+ message_loop.PostMessage(message);
+}
+
+void NXFileObject_ProgressiveDownloader::OnFileSize(uint64_t size)
+{
+ size_message_t *message = (size_message_t *)message_loop.AllocateMessage();
+ message->message = MESSAGE_SIZE;
+ message->size = size;
+ message_loop.PostMessage(message);
+}
+
+void NXFileObject_ProgressiveDownloader::OnClosed()
+{
+ nu::message_node_t *message = message_loop.AllocateMessage();
+ message->message = MESSAGE_CLOSED;
+ message_loop.PostMessage(message);
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::Initialize(nx_uri_t uri, jnl_http_t http, const char *user_agent)
+{
+ ReferenceCountedNXURI temp_uri;
+ NXURICreateTemp(&temp_uri);
+ ns_error_t ret = download.Initialize(uri, http, user_agent, temp_uri);
+ if (ret != NErr_Success)
+ {
+ closed=true;
+ return ret;
+ }
+
+ progressive_file_read = NXFile_fopen(temp_uri, nx_file_FILE_read_binary);
+
+ for (;;)
+ {
+ Wait(10);
+ if (error_code != NErr_Success)
+ return error_code;
+
+ if (connected)
+ break;
+ }
+ return NErr_Success;
+}
+
+void NXFileObject_ProgressiveDownloader::ProcessMessage(nu::message_node_t *message)
+{
+ switch(message->message)
+ {
+ case MESSAGE_ERROR:
+ {
+ error_message_t *seek_message = (error_message_t *)message;
+ error_code = seek_message->error_code;
+ }
+ break;
+ case MESSAGE_CONNECTED:
+ connected = true;
+ break;
+ case MESSAGE_SIZE:
+ {
+ size_message_t *seek_message = (size_message_t *)message;
+ region.end = seek_message->size;
+ }
+ break;
+ case MESSAGE_CLOSED:
+ closed=true;
+ break;
+ }
+
+ message_loop.FreeMessage(message);
+}
+
+void NXFileObject_ProgressiveDownloader::Wait(unsigned int milliseconds)
+{
+ for (;;)
+ {
+ nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
+ if (message)
+ ProcessMessage(message);
+ else
+ break;
+ }
+
+ nu::message_node_t *message = message_loop.PeekMessage(milliseconds);
+ if (message)
+ ProcessMessage(message);
+}
+
+bool NXFileObject_ProgressiveDownloader::WaitForRead(uint64_t size)
+{
+ if (progress_tracker.Valid(position, position+size))
+ return true;
+
+ if (need_seek)
+ {
+ // give it just a little bit of time to avoid constant reseeks when the download thread is just barely keeping up
+ Wait(10);
+ if (progress_tracker.Valid(position, position+size))
+ return true;
+
+ connected=false;
+ error_code=NErr_Success;
+ download.Seek(position, (uint64_t)position+size);
+
+ for (;;)
+ {
+ Wait(10);
+ if (error_code != NErr_Success)
+ return false;
+
+ if (connected)
+ break;
+ }
+ }
+
+ while (!progress_tracker.Valid(position, position+size))
+ {
+ Wait(10);
+ }
+
+ return true;
+}
+
+
+ns_error_t NXFileObject_ProgressiveDownloader::Read(void *buffer, size_t bytes_requested, size_t *bytes_read)
+{
+ if (end_of_file || position >= (region.end - region.start))
+ return NErr_EndOfFile;
+
+ // don't allow a read past the end of the file as this will confuse progress_tracker (which doesn't know/care about the file length)
+ if ((position + bytes_requested) > region.end)
+ bytes_requested = (size_t)(region.end - position);
+
+ if (WaitForRead((uint64_t)bytes_requested) == false)
+ {
+ *bytes_read = 0;
+ return error_code;
+ }
+
+ if (need_seek)
+ {
+ _fseeki64(progressive_file_read, position, SEEK_SET);
+ need_seek=false;
+ }
+
+ /* TODO: benski> if r < bytes_requested, then we need to flush the buffer.
+ on windows, we can use fflush(progressive_file_read)
+ on other platforms it's not guaranteed! */
+ size_t r = fread(buffer, 1, bytes_requested, progressive_file_read);
+ this->position += r;
+ *bytes_read = r;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::Seek(uint64_t new_position)
+{
+ if (new_position >= (region.end - region.start))
+ {
+ this->position = region.end - region.start;
+ end_of_file=true;
+ }
+ else
+ {
+ if (new_position == position)
+ return NErr_Success;
+ position = new_position;
+ need_seek=true;
+
+ end_of_file=false;
+ }
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::Tell(uint64_t *position)
+{
+ if (end_of_file)
+ *position = region.end - region.start;
+ else
+ *position = this->position - region.start;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::PeekByte(uint8_t *byte)
+{
+ if (position == region.end)
+ return NErr_EndOfFile;
+
+ // make sure we have enough room
+ if (WaitForRead((uint64_t)1) == false)
+ return error_code;
+
+ if (need_seek)
+ {
+ _fseeki64(progressive_file_read, position, SEEK_SET);
+ need_seek=false;
+ }
+
+ int read_byte = fgetc(progressive_file_read);
+ if (read_byte != EOF)
+ ungetc(read_byte, progressive_file_read);
+ else
+ {
+ /* TODO: benski> if we hit the point, then we actually need to flush the buffer.
+ on some platforms, fflush(progressive_file_read) will do that, but it's not guaranteed! */
+ return NErr_EndOfFile;
+ }
+
+ *byte = (uint8_t)read_byte;
+ return NErr_Success;
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::Sync()
+{
+ return NErr_NotImplemented;
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::Truncate()
+{
+ return NErr_NotImplemented;
+}
+
+ns_error_t NXFileObject_ProgressiveDownloader::Write(const void *buffer, size_t bytes)
+{
+ return NErr_NotImplemented;
+}
+
+bool NXFileObject_ProgressiveDownloader::Available(uint64_t size, uint64_t *available)
+{
+ uint64_t end = position+size;
+ if (end > region.end)
+ end = region.end;
+ if (position == region.end)
+ {
+ if (available)
+ *available=0;
+ return true;
+ }
+ return progress_tracker.Valid(position, end, available);
+}
+
+ns_error_t NXFileOpenProgressiveDownloader(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags, jnl_http_t http, const char *user_agent)
+{
+ NXFileObject_ProgressiveDownloader *file_object = new (std::nothrow) NXFileObject_ProgressiveDownloader;
+ if (!file_object)
+ return NErr_OutOfMemory;
+
+ ns_error_t ret = file_object->Initialize(filename, http, user_agent);
+ if (ret != NErr_Success)
+ {
+ delete file_object;
+ return ret;
+ }
+
+ *out_file = (nx_file_t)file_object;
+ return NErr_Success;
+}
+
+ns_error_t NXFileProgressiveDownloaderAvailable(nx_file_t _f, uint64_t size, uint64_t *available)
+{
+ if (!_f)
+ return NErr_BadParameter;
+
+ NXFileObject_ProgressiveDownloader *f = (NXFileObject_ProgressiveDownloader *)_f;
+ if (f->Available(size, available))
+ return NErr_True;
+ else
+ return NErr_False;
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/NXZipFile.cpp b/Src/replicant/nx/win/NXZipFile.cpp
new file mode 100644
index 000000000..71b00a519
--- /dev/null
+++ b/Src/replicant/nx/win/NXZipFile.cpp
@@ -0,0 +1,209 @@
+#include "NXFileObject.h"
+#include <new>
+#include "minizip/unzip.h"
+#include <nx/nxfile.h>
+#include <assert.h>
+
+class NXZipFile : NXFileObject
+{
+public:
+ NXZipFile(unzFile zip_file);
+ ~NXZipFile();
+
+ /* NXFileObject implementation */
+ ns_error_t Read(void *buffer, size_t bytes_requested, size_t *bytes_read);
+ ns_error_t Write(const void *buffer, size_t bytes);
+ ns_error_t Seek(uint64_t position);
+ ns_error_t Tell(uint64_t *position);
+ ns_error_t PeekByte(uint8_t *byte);
+ ns_error_t Sync();
+ ns_error_t Truncate();
+ // TODO(benski) implementation EOF
+ // TODO(benski) implement region locking
+private:
+ unzFile zip_file;
+};
+
+NXZipFile::NXZipFile(unzFile zip_file) : zip_file(zip_file)
+{
+}
+
+NXZipFile::~NXZipFile()
+{
+ if (zip_file) {
+ unzCloseCurrentFile(zip_file);
+ unzClose(zip_file);
+ zip_file=0;
+ }
+}
+
+/* NXFileObject implementation */
+ns_error_t NXZipFile::Read(void *buffer, size_t bytes_requested, size_t *bytes_read)
+{
+ int zret = unzReadCurrentFile(zip_file, buffer, (unsigned int)bytes_requested);
+ if (zret == 0) {
+ if (bytes_read) {
+ *bytes_read = 0;
+ }
+ return NErr_EndOfFile;
+ } else if (zret > 0) {
+ if (bytes_read) {
+ *bytes_read = (size_t)zret;
+ }
+ return NErr_Success;
+ } else {
+ if (bytes_read) {
+ *bytes_read = 0;
+ }
+ return NErr_Error;
+ }
+}
+
+ns_error_t NXZipFile::Write(const void *buffer, size_t bytes)
+{
+ return NErr_NotImplemented;
+}
+
+ns_error_t NXZipFile::Seek(uint64_t position)
+{
+ // TODO(benski) error check)
+ unzSetOffset64(zip_file, position);
+ return NErr_Success;
+}
+
+ns_error_t NXZipFile::Tell(uint64_t *position)
+{
+ *position = unzGetOffset64(zip_file);
+ return NErr_Success;
+}
+
+ns_error_t NXZipFile::PeekByte(uint8_t *byte)
+{
+ return NErr_NotImplemented;
+}
+
+ns_error_t NXZipFile::Sync()
+{
+ return NErr_NotImplemented;
+}
+
+ns_error_t NXZipFile::Truncate()
+{
+ return NErr_NotImplemented;
+}
+
+static voidpf ZCALLBACK unzip_nxfile_open OF((voidpf opaque, const void* filename, int mode))
+{
+ nx_file_t f;
+ if (NXFileOpenFile(&f, (nx_uri_t)filename, nx_file_FILE_read_binary) != NErr_Success) {
+ return 0;
+ }
+ return f;
+}
+
+static uLong ZCALLBACK unzip_nxfile_read OF((voidpf opaque, voidpf stream, void* buf, uLong size))
+{
+ nx_file_t f = (nx_file_t)stream;
+ size_t bytes_read;
+ if (NXFileRead(f, buf, size, &bytes_read) != NErr_Success) {
+ return 0;
+ }
+ return (uLong)bytes_read;
+}
+
+static int ZCALLBACK unzip_nxfile_close OF((voidpf opaque, voidpf stream))
+{
+ NXFileRelease((nx_file_t)stream);
+ return 0;
+}
+
+
+static ZPOS64_T ZCALLBACK unzip_nxfile_tell OF((voidpf opaque, voidpf stream))
+{
+ nx_file_t f = (nx_file_t)stream;
+ uint64_t position;
+ if (NXFileTell(f, &position) == NErr_Success) {
+ return (int64_t)position;
+ } else {
+ return -1;
+ }
+}
+
+static long ZCALLBACK unzip_nxfile_seek OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int whence))
+{
+ nx_file_t f = (nx_file_t)stream;
+ uint64_t position;
+ if (whence == SEEK_SET) {
+ position = offset;
+ } else if (whence == SEEK_CUR) {
+ ns_error_t err = NXFileTell(f, &position);
+ if (err != NErr_Success) {
+ return -1;
+ }
+ position += offset;
+ } else if (whence == SEEK_END) {
+ uint64_t length;
+ NXFileLength(f, &length);
+ position = length + offset;
+ } else {
+ return -1;
+ }
+ ns_error_t err = NXFileSeek(f, position);
+ if (err == NErr_Success) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+#if 0
+ open64_file_func zopen64_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell64_file_func ztell64_file;
+ seek64_file_func zseek64_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+#endif
+
+ns_error_t NXFileOpenZip(nx_file_t *out_file, nx_uri_t filename, nx_string_t extension_hint)
+{
+#if 0
+ typedef struct zlib_filefunc_def_s
+{
+ open_file_func zopen_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell_file_func ztell_file;
+ seek_file_func zseek_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+ voidpf opaque;
+} zlib_filefunc_def;
+#endif
+
+ zlib_filefunc64_def file_func = {0, };
+ file_func.zopen64_file = unzip_nxfile_open;
+ file_func.zread_file = unzip_nxfile_read;
+ file_func.ztell64_file = unzip_nxfile_tell;
+ file_func.zseek64_file = unzip_nxfile_seek;
+ file_func.zclose_file = unzip_nxfile_close;
+
+ unzFile zip_file = unzOpen2_64(filename, &file_func);
+ if (zip_file == NULL) {
+ return NErr_Error;
+ }
+
+ unzGoToFirstFile(zip_file);
+ // TODO(benski): look for filename with extension_hint as extension
+ // TODO(benski): search for anything with extension
+ unzOpenCurrentFile(zip_file);
+
+ NXZipFile *nx_zip_file = new (std::nothrow) NXZipFile(zip_file);
+ if (!nx_zip_file) {
+ unzCloseCurrentFile(zip_file);
+ unzClose(zip_file);
+ return NErr_OutOfMemory;
+ }
+ *out_file = (nx_file_t)nx_zip_file;
+ return NErr_Success;
+}
diff --git a/Src/replicant/nx/win/nxapi.h b/Src/replicant/nx/win/nxapi.h
new file mode 100644
index 000000000..be8bf2ec1
--- /dev/null
+++ b/Src/replicant/nx/win/nxapi.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "../../foundation/guid.h"
+
+#ifdef NX_EXPORTS
+#define NX_API __declspec(dllexport)
+#else
+#define NX_API __declspec(dllimport)
+#endif
+
+/* increment this any time that the NX API changes in a non-backwards-compatible way (preferably rarely) */
+static const int nx_api_version = 1;
+
+// {E7079A4B-BBB3-441F-ADCD-E0F1FE276EE3}
+static const GUID nx_platform_guid =
+{ 0xe7079a4b, 0xbbb3, 0x441f, { 0xad, 0xcd, 0xe0, 0xf1, 0xfe, 0x27, 0x6e, 0xe3 } };
diff --git a/Src/replicant/nx/win/nxcondition.c b/Src/replicant/nx/win/nxcondition.c
new file mode 100644
index 000000000..1e3ef0094
--- /dev/null
+++ b/Src/replicant/nx/win/nxcondition.c
@@ -0,0 +1,66 @@
+#include "nxcondition.h"
+#include "foundation/error.h"
+
+int NXConditionInitialize(nx_condition_t condition)
+{
+ if (condition == 0)
+ return NErr_NullPointer;
+
+ InitializeCriticalSection(&condition->mutex);
+ InitializeConditionVariable(&condition->condition);
+ return NErr_Success;
+}
+
+int NXConditionDestroy(nx_condition_t condition)
+{
+ if (condition == 0)
+ return NErr_NullPointer;
+
+ DeleteCriticalSection(&condition->mutex);
+ return NErr_Success;
+}
+
+int NXConditionLock(nx_condition_t condition)
+{
+ if (condition == 0)
+ return NErr_NullPointer;
+
+ EnterCriticalSection(&condition->mutex);
+ return NErr_Success;
+}
+
+int NXConditionUnlock(nx_condition_t condition)
+{
+if (condition == 0)
+ return NErr_NullPointer;
+
+ LeaveCriticalSection(&condition->mutex);
+ return NErr_Success;
+}
+
+int NXConditionWait(nx_condition_t condition)
+{
+ if (condition == 0)
+ return NErr_NullPointer;
+
+ SleepConditionVariableCS(&condition->condition, &condition->mutex, INFINITE);
+ return NErr_Success;
+}
+
+int NXConditionTimedWait(nx_condition_t condition, unsigned int milliseconds)
+{
+ if (condition == 0)
+ return NErr_NullPointer;
+
+ SleepConditionVariableCS(&condition->condition, &condition->mutex, milliseconds);
+ return NErr_Success;
+}
+
+int NXConditionSignal(nx_condition_t condition)
+{
+ if (condition == 0)
+ return NErr_NullPointer;
+
+ WakeConditionVariable(&condition->condition);
+ return NErr_Success;
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxcondition.h b/Src/replicant/nx/win/nxcondition.h
new file mode 100644
index 000000000..42a634aa1
--- /dev/null
+++ b/Src/replicant/nx/win/nxcondition.h
@@ -0,0 +1,28 @@
+#pragma once
+#include "nx/nxapi.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct nx_condition_struct_t
+{
+ CONDITION_VARIABLE condition;
+ CRITICAL_SECTION mutex;
+} nx_condition_value_t, *nx_condition_t;
+
+NX_API int NXConditionInitialize(nx_condition_t condition);
+NX_API int NXConditionDestroy(nx_condition_t condition);
+NX_API int NXConditionLock(nx_condition_t condition);
+NX_API int NXConditionUnlock(nx_condition_t condition);
+NX_API int NXConditionWait(nx_condition_t condition);
+NX_API int NXConditionTimedWait(nx_condition_t condition, unsigned int milliseconds);
+NX_API int NXConditionSignal(nx_condition_t condition);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxdata.c b/Src/replicant/nx/win/nxdata.c
new file mode 100644
index 000000000..065eddbe4
--- /dev/null
+++ b/Src/replicant/nx/win/nxdata.c
@@ -0,0 +1,276 @@
+#include "nx/nxdata.h"
+#include "foundation/atomics.h"
+#include "foundation/error.h"
+#include "nx/nxfile.h"
+#include <sys/stat.h>
+
+/* windows implementation */
+struct nx_data_struct_t
+{
+ volatile size_t ref_count;
+ nx_string_t mime_type;
+ nx_string_t description;
+ nx_uri_t source_uri;
+ nx_file_stat_t source_stats;
+ size_t len;
+ uint8_t data[1];
+};
+
+static size_t NXDataMallocSize(size_t bytes)
+{
+ /* TODO: overflow check? */
+ const nx_data_t dummy=0;
+ size_t header = (size_t)&dummy->data[0] - (size_t)dummy;
+ return header + bytes;
+}
+
+nx_data_t NXDataRetain(nx_data_t data)
+{
+ if (!data)
+ return 0;
+
+ nx_atomic_inc(&data->ref_count);
+ return data;
+}
+
+void NXDataRelease(nx_data_t data)
+{
+ if (data)
+ {
+ if (nx_atomic_dec(&data->ref_count) == 0)
+ {
+ free(data->source_stats);
+ NXURIRelease(data->source_uri);
+ NXStringRelease(data->mime_type);
+ NXStringRelease(data->description);
+ free(data);
+ }
+ }
+}
+
+int NXDataCreate(nx_data_t *out_data, const void *bytes, size_t length)
+{
+ void *new_bytes;
+ int ret = NXDataCreateWithSize(out_data, &new_bytes, length);
+ if (ret != NErr_Success)
+ return ret;
+
+ memcpy(new_bytes, bytes, length);
+ return NErr_Success;
+}
+
+int NXDataCreateWithSize(nx_data_t *out_data, void **bytes, size_t length)
+{
+ nx_data_t data = 0;
+ size_t data_length = NXDataMallocSize(length);
+ data = (nx_data_t)malloc(data_length);
+ if (!data)
+ return NErr_OutOfMemory;
+
+ data->ref_count = 1;
+ data->len = length;
+ data->mime_type=0;
+ data->source_uri=0;
+ data->source_stats=0;
+ data->description=0;
+ if (bytes)
+ *bytes = data->data;
+ *out_data=data;
+ return NErr_Success;
+}
+
+int NXDataCreateEmpty(nx_data_t *out_data)
+{
+ return NXDataCreateWithSize(out_data, 0, 0);
+}
+
+int NXDataCreateFromURI(nx_data_t *out_data, nx_uri_t filename)
+{
+ nx_file_stat_s stat_buffer;
+ nx_data_t data;
+ size_t data_length;
+ size_t bytes_read;
+ uint64_t file_length;
+ void *bytes;
+ int ret;
+ int fd;
+
+
+ fd = NXFile_open(filename, nx_file_O_BINARY|nx_file_O_RDONLY);
+ if (fd == -1)
+ return NErr_FileNotFound;
+
+ ret = NXFile_fstat(fd, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ close(fd);
+ return ret;
+ }
+
+ file_length = stat_buffer.file_size;
+
+ if (file_length > SIZE_MAX)
+ {
+ close(fd);
+ return NErr_IntegerOverflow;
+ }
+
+ data_length = (size_t)file_length;
+
+ ret = NXDataCreateWithSize(&data, &bytes, data_length);
+ if (ret != NErr_Success)
+ {
+ close(fd);
+ return ret;
+ }
+
+ data->source_stats=(nx_file_stat_t)malloc(sizeof(nx_file_stat_s));
+ if (!data->source_stats)
+ {
+ close(fd);
+ NXDataRelease(data);
+ return NErr_OutOfMemory;
+ }
+
+ bytes_read = read(fd, bytes, (int)data_length);
+ close(fd);
+ if (bytes_read != data_length)
+ {
+ NXDataRelease(data);
+ return NErr_Error;
+ }
+
+ *data->source_stats=stat_buffer;
+ data->source_uri=NXURIRetain(filename);
+ *out_data = data;
+ return NErr_Success;
+}
+
+int NXDataGet(nx_data_t data, const void **bytes, size_t *length)
+{
+ if (!data)
+ return NErr_BadParameter;
+
+ if (data->len == 0)
+ return NErr_Empty;
+
+ *bytes = data->data;
+ *length = data->len;
+ return NErr_Success;
+}
+
+size_t NXDataSize(nx_data_t data)
+{
+ if (!data)
+ return 0;
+
+ return data->len;
+}
+
+int NXDataSetMIME(nx_data_t data, nx_string_t mime_type)
+{
+ nx_string_t old;
+ if (!data)
+ return NErr_BadParameter;
+
+ old = data->mime_type;
+ data->mime_type = NXStringRetain(mime_type);
+ NXStringRelease(old);
+ return NErr_Success;
+}
+
+int NXDataSetDescription(nx_data_t data, nx_string_t description)
+{
+ nx_string_t old;
+ if (!data)
+ return NErr_BadParameter;
+
+ old = data->description;
+ data->description = NXStringRetain(description);
+ NXStringRelease(old);
+ return NErr_Success;
+}
+
+int NXDataSetSourceURI(nx_data_t data, nx_uri_t source_uri)
+{
+ nx_uri_t old;
+ if (!data)
+ return NErr_BadParameter;
+
+ old = data->source_uri;
+ data->source_uri = NXURIRetain(source_uri);
+ NXURIRelease(old);
+ return NErr_Success;
+}
+
+int NXDataSetSourceStat(nx_data_t data, nx_file_stat_t source_stats)
+{
+ nx_file_stat_t new_stats;
+ if (!data)
+ return NErr_BadParameter;
+
+ if (source_stats)
+ {
+ new_stats=(nx_file_stat_t)malloc(sizeof(nx_file_stat_s));
+ if (!new_stats)
+ return NErr_OutOfMemory;
+
+ *new_stats = *source_stats;
+ free(data->source_stats);
+ data->source_stats=new_stats;
+ }
+ else
+ {
+ free(data->source_stats);
+ data->source_stats=0;
+ }
+ return NErr_Success;
+}
+
+int NXDataGetMIME(nx_data_t data, nx_string_t *mime_type)
+{
+ if (!data)
+ return NErr_BadParameter;
+
+ if (!data->mime_type)
+ return NErr_Empty;
+
+ *mime_type = NXStringRetain(data->mime_type);
+ return NErr_Success;
+}
+
+int NXDataGetDescription(nx_data_t data, nx_string_t *description)
+{
+ if (!data)
+ return NErr_BadParameter;
+
+ if (!data->description)
+ return NErr_Empty;
+
+ *description = NXStringRetain(data->description);
+ return NErr_Success;
+}
+
+int NXDataGetSourceURI(nx_data_t data, nx_uri_t *source_uri)
+{
+ if (!data)
+ return NErr_BadParameter;
+
+ if (!data->source_uri)
+ return NErr_Empty;
+
+ *source_uri = NXURIRetain(data->source_uri);
+ return NErr_Success;
+}
+
+int NXDataGetSourceStat(nx_data_t data, nx_file_stat_t *source_stats)
+{
+ if (!data)
+ return NErr_BadParameter;
+
+ if (!data->source_stats)
+ return NErr_Empty;
+
+ *source_stats = data->source_stats;
+ return NErr_Success;
+}
diff --git a/Src/replicant/nx/win/nxdata.h b/Src/replicant/nx/win/nxdata.h
new file mode 100644
index 000000000..1a5eee947
--- /dev/null
+++ b/Src/replicant/nx/win/nxdata.h
@@ -0,0 +1,42 @@
+#pragma once
+#include "../../foundation/types.h"
+#include "../../nx/nxuri.h"
+#include "../../nx/nxapi.h"
+#include "../../nx/nxfile.h"
+#include "../../nx/nxstring.h"
+
+/* windows implementation */
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef struct nx_data_struct_t *nx_data_t;
+
+ NX_API nx_data_t NXDataRetain(nx_data_t data);
+ NX_API void NXDataRelease(nx_data_t data);
+
+ NX_API int NXDataCreate(nx_data_t *data, const void *bytes, size_t length);
+ NX_API int NXDataCreateWithSize(nx_data_t *data, void **bytes, size_t length);
+ /* creates an empty data object. useful if you need to store MIME, source URI, etc. without having actual data */
+ NX_API int NXDataCreateEmpty(nx_data_t *data);
+ /* you can pass file_stats==NULL if you don't care about the file stats */
+ NX_API int NXDataCreateFromURI(nx_data_t *data, nx_uri_t filename);
+
+ NX_API size_t NXDataSize(nx_data_t data);
+ NX_API int NXDataGet(nx_data_t data, const void **bytes, size_t *length);
+
+ /* You can _only_ call these on your own nx_data_t object _before_ you give it to anyone else! */
+ NX_API int NXDataSetMIME(nx_data_t data, nx_string_t mime_type);
+ NX_API int NXDataSetDescription(nx_data_t data, nx_string_t description);
+ NX_API int NXDataSetSourceURI(nx_data_t data, nx_uri_t source_uri);
+ NX_API int NXDataSetSourceStat(nx_data_t data, nx_file_stat_t source_stats);
+
+ /* you need to call NXStringRelease on what you get back (if the function succeeded) */
+ NX_API int NXDataGetMIME(nx_data_t data, nx_string_t *mime_type);
+ NX_API int NXDataGetDescription(nx_data_t data, nx_string_t *description);
+ NX_API int NXDataGetSourceURI(nx_data_t data, nx_uri_t *source_uri);
+ NX_API int NXDataGetSourceStat(nx_data_t data, nx_file_stat_t *source_stats);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nx/win/nxfile.c b/Src/replicant/nx/win/nxfile.c
new file mode 100644
index 000000000..dbf790891
--- /dev/null
+++ b/Src/replicant/nx/win/nxfile.c
@@ -0,0 +1,68 @@
+#include "nxfile.h"
+#include "foundation/error.h"
+#include <sys/stat.h>
+ns_error_t NXFile_move(nx_uri_t destination, nx_uri_t source)
+{
+ if (!ReplaceFile(destination->string, source->string, 0, 0, 0, 0))
+ {
+ if (!MoveFile(source->string, destination->string))
+ {
+ if (!CopyFile(source->string, destination->string, FALSE))
+ {
+ return NErr_Error;
+ }
+ DeleteFile(source->string);
+ }
+ }
+ return NErr_Success;
+}
+
+ns_error_t NXFile_unlink(nx_uri_t filename)
+{
+ if (DeleteFile(filename->string))
+ return NErr_Success;
+ else
+ return NErr_Error;
+}
+
+ns_error_t NXFile_stat(nx_uri_t filename, nx_file_stat_t file_stats)
+{
+ struct __stat64 buffer;
+
+ if (_wstat64(filename->string, &buffer) == 0)
+ {
+ file_stats->access_time = buffer.st_atime;
+ file_stats->creation_time = buffer.st_ctime;
+ file_stats->modified_time = buffer.st_mtime;
+ file_stats->file_size = buffer.st_size;
+ return NErr_Success;
+ }
+ else
+ return NErr_Error;
+}
+
+ns_error_t NXFile_statFILE(FILE *f, nx_file_stat_t file_stats)
+{
+ int fd = _fileno(f);
+ if (fd == -1)
+ return NErr_Error;
+
+ return NXFile_fstat(fd, file_stats);
+}
+
+ns_error_t NXFile_fstat(int file_descriptor, nx_file_stat_t file_stats)
+{
+ struct __stat64 buffer;
+
+ if (_fstat64(file_descriptor, &buffer) == 0)
+ {
+ file_stats->access_time = buffer.st_atime;
+ file_stats->creation_time = buffer.st_ctime;
+ file_stats->modified_time = buffer.st_mtime;
+ file_stats->file_size = buffer.st_size;
+ return NErr_Success;
+ }
+ else
+ return NErr_Error;
+}
+
diff --git a/Src/replicant/nx/win/nxfile.h b/Src/replicant/nx/win/nxfile.h
new file mode 100644
index 000000000..d7d943e26
--- /dev/null
+++ b/Src/replicant/nx/win/nxfile.h
@@ -0,0 +1,116 @@
+#pragma once
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include "../../nx/nxapi.h"
+#include <stdio.h> // for FILE
+
+#include "../../nx/nxuri.h"
+#include <io.h>
+#include <fcntl.h>
+#include "../../nx/nxtime.h"
+#include "../../foundation/error.h"
+#include "../../jnetlib/jnetlib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct nx_file_stat_s
+ {
+ nx_time_unix_64_t creation_time;
+ nx_time_unix_64_t access_time;
+ nx_time_unix_64_t modified_time;
+ uint64_t file_size;
+ } nx_file_stat_s, *nx_file_stat_t;
+
+ typedef enum
+ {
+ nx_file_FILE_none = 0,
+ nx_file_FILE_binary = (1 << 0),
+ nx_file_FILE_read_text= (1 << 1),
+ nx_file_FILE_read_binary=nx_file_FILE_read_text|nx_file_FILE_binary,
+ nx_file_FILE_write_text=(1 << 2),
+ nx_file_FILE_write_binary=nx_file_FILE_write_text|nx_file_FILE_binary,
+ nx_file_FILE_update_text=(1 << 3),
+ nx_file_FILE_update_binary=nx_file_FILE_update_text|nx_file_FILE_binary,
+ nx_file_FILE_readwrite_text=(1 << 4),
+ nx_file_FILE_readwrite_binary=nx_file_FILE_readwrite_text|nx_file_FILE_binary,
+
+ nx_file_FILE_writable_mask = nx_file_FILE_write_text|nx_file_FILE_update_text|nx_file_FILE_readwrite_text,
+ } nx_file_FILE_flags_t;
+
+ static const int nx_file_O_BINARY=_O_BINARY;
+ static const int nx_file_O_WRONLY=_O_WRONLY;
+ static const int nx_file_O_RDONLY=_O_RDONLY;
+
+ static FILE *NXFile_fopen(nx_uri_t filename, nx_file_FILE_flags_t flags)
+ {
+ if (filename)
+ {
+ if (flags == nx_file_FILE_read_binary)
+ {
+ return _wfopen(filename->string, L"rb");
+ }
+ else if (flags == nx_file_FILE_write_binary)
+ {
+ return _wfopen(filename->string, L"wb");
+ }
+ else if (flags == nx_file_FILE_update_binary)
+ {
+ return _wfopen(filename->string, L"r+b");
+ }
+ else if (flags == nx_file_FILE_readwrite_binary)
+ {
+ return _wfopen(filename->string, L"w+b");
+ }
+ }
+ return 0;
+ }
+
+ /* returns a file descriptor */
+ static int NXFile_open(nx_uri_t filename, int flags)
+ {
+ return _wopen(filename->string, flags);
+ }
+
+ NX_API ns_error_t NXFile_move(nx_uri_t destination, nx_uri_t source);
+ NX_API ns_error_t NXFile_unlink(nx_uri_t filename);
+ NX_API ns_error_t NXFile_stat(nx_uri_t filename, nx_file_stat_t file_stats);
+ NX_API ns_error_t NXFile_statFILE(FILE *f, nx_file_stat_t file_stats);
+ NX_API ns_error_t NXFile_fstat(int file_descriptor, nx_file_stat_t file_stats);
+
+ /* --------------------------------------------------------------------------- */
+ typedef struct nx_file_s { size_t dummy; } *nx_file_t;
+ NX_API ns_error_t NXFileOpenFile(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags);
+ NX_API ns_error_t NXFileOpenProgressiveDownloader(nx_file_t *out_file, nx_uri_t filename, nx_file_FILE_flags_t flags, jnl_http_t http, const char *user_agent);
+ NX_API ns_error_t NXFileOpenZip(nx_file_t *out_file, nx_uri_t filename, nx_string_t extension_hint);
+ NX_API nx_file_t NXFileRetain(nx_file_t f);
+ NX_API void NXFileRelease(nx_file_t f);
+ /* the implementation of this function will only return NErr_EndOfFile if 0 bytes were read.
+ when *bytes_read < bytes_requested, it's likely that the file is at the end, but it will still return NErr_Success
+ until the next call */
+ NX_API ns_error_t NXFileRead(nx_file_t f, void *buffer, size_t bytes_requested, size_t *bytes_read);
+ NX_API ns_error_t NXFileWrite(nx_file_t f, const void *buffer, size_t bytes);
+ NX_API ns_error_t NXFileSeek(nx_file_t f, uint64_t position);
+ NX_API ns_error_t NXFileTell(nx_file_t f, uint64_t *position);
+ NX_API ns_error_t NXFileLockRegion(nx_file_t _f, uint64_t start_position, uint64_t end_position);
+ NX_API ns_error_t NXFileUnlockRegion(nx_file_t _f);
+ /* file_stats does _not_ take into account the current region */
+ NX_API ns_error_t NXFileStat(nx_file_t f, nx_file_stat_t file_stats);
+ /* returns the length of the file given the current region */
+ NX_API ns_error_t NXFileLength(nx_file_t f, uint64_t *length);
+ /* returns NErr_True, NErr_False, or possibly some error */
+ NX_API ns_error_t NXFileEndOfFile(nx_file_t f);
+ /* this exists as a one-off for nsmp4. hopefully we can get rid of it */
+ NX_API ns_error_t NXFilePeekByte(nx_file_t f, uint8_t *byte);
+ NX_API ns_error_t NXFileSync(nx_file_t f);
+ NX_API ns_error_t NXFileTruncate(nx_file_t f);
+
+ /* only valid for Progressive Downloader objects */
+ NX_API ns_error_t NXFileProgressiveDownloaderAvailable(nx_file_t f, uint64_t size, uint64_t *available);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nximage.c b/Src/replicant/nx/win/nximage.c
new file mode 100644
index 000000000..465860026
--- /dev/null
+++ b/Src/replicant/nx/win/nximage.c
@@ -0,0 +1,34 @@
+#include "nximage.h"
+
+static HANDLE image_heap;
+void NXImageSetHeap(HANDLE _image_heap)
+{
+ if (!image_heap)
+ image_heap = _image_heap;
+}
+
+static size_t NXImageMallocSize(size_t bytes)
+{
+ return sizeof(nx_image_s) + bytes - sizeof(ARGB32);
+}
+
+nx_image_t NXImageMalloc(uint32_t width, uint32_t height)
+{
+ size_t bytes;
+ nx_image_t img;
+ bytes = width*height*4; // TODO: overflow check
+
+ img = (nx_image_t)malloc(NXImageMallocSize(bytes));
+ img->ref_count = 1;
+ img->len = bytes;
+ img->width = width;
+ img->height = height;
+ return img;
+
+}
+
+nx_image_t NXImageRetain(nx_image_t image)
+{
+ image->ref_count++;
+ return image;
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nximage.h b/Src/replicant/nx/win/nximage.h
new file mode 100644
index 000000000..eab587f17
--- /dev/null
+++ b/Src/replicant/nx/win/nximage.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "foundation/types.h"
+#include "nxapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct nx_image
+{
+ size_t ref_count;
+ uint32_t width;
+ uint32_t height;
+ size_t len;
+ ARGB32 image[1];
+} nx_image_s, *nx_image_t;
+
+NX_API void NXImageSetHeap(HANDLE image_heap);
+NX_API nx_image_t NXImageMalloc(uint32_t width, uint32_t height);
+NX_API nx_image_t NXImageRetain(nx_image_t image);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxlog.c b/Src/replicant/nx/win/nxlog.c
new file mode 100644
index 000000000..7e8da01b5
--- /dev/null
+++ b/Src/replicant/nx/win/nxlog.c
@@ -0,0 +1,19 @@
+#include "nxlog.h"
+#include <stdio.h>
+#include <stdarg.h>
+//#include <windows.h>
+
+static char *nx_log_tag = "libreplicant";
+#define MAX_FMT_SIZE 512
+
+void NXLog(int priority, char *fmt, ...){
+
+ char formatted_string[MAX_FMT_SIZE];
+
+ va_list argptr;
+ va_start(argptr,fmt);
+ vsnprintf(formatted_string, MAX_FMT_SIZE, fmt, argptr);
+ va_end(argptr);
+
+// OutputDebugString(formatted_string);
+}
diff --git a/Src/replicant/nx/win/nxlog.h b/Src/replicant/nx/win/nxlog.h
new file mode 100644
index 000000000..9e3a41ad0
--- /dev/null
+++ b/Src/replicant/nx/win/nxlog.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "foundation/types.h"
+#include "nx/nxapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NX_API void NXLog(int priority, char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nx/win/nxmutablestring.c b/Src/replicant/nx/win/nxmutablestring.c
new file mode 100644
index 000000000..1e11bdbaa
--- /dev/null
+++ b/Src/replicant/nx/win/nxmutablestring.c
@@ -0,0 +1,40 @@
+#include "nxmutablestring.h"
+#include "foundation/error.h"
+
+/* currently this is closely coupled with the nx_string_t implementation. beware! */
+extern HANDLE string_heap;
+
+void NXMutableStringDestroy(nx_mutable_string_t mutable_string)
+{
+ if (mutable_string)
+ {
+ if (mutable_string->nx_string_data)
+ NXStringRelease(mutable_string->nx_string_data);
+ HeapFree(string_heap, 0, mutable_string);
+ }
+}
+
+nx_mutable_string_t NXMutableStringCreateFromXML(const nsxml_char_t *characters, size_t num_characters)
+{
+ nx_mutable_string_t mutable_string = (nx_mutable_string_t)HeapAlloc(string_heap, 0, sizeof(nx_mutable_string_struct_t));
+ NXStringCreateWithBytes(&mutable_string->nx_string_data, characters, num_characters*2, nx_charset_utf16le);
+ mutable_string->allocation_length = num_characters;
+ return mutable_string;
+}
+
+int NXMutableStringGrowFromXML(nx_mutable_string_t mutable_string, const nsxml_char_t *characters, size_t num_characters)
+{
+ if (mutable_string->nx_string_data->len + num_characters + 1 > mutable_string->allocation_length)
+ {
+ nx_string_t new_string = NXStringRealloc(mutable_string->nx_string_data, mutable_string->nx_string_data->len + num_characters + 1);
+ if (!new_string)
+ return NErr_OutOfMemory;
+ mutable_string->nx_string_data = new_string;
+ mutable_string->allocation_length = mutable_string->nx_string_data->len + num_characters + 1;
+ }
+ memcpy(mutable_string->nx_string_data->string + mutable_string->nx_string_data->len, characters, num_characters*sizeof(wchar_t));
+ mutable_string->nx_string_data->len += num_characters;
+ mutable_string->nx_string_data->string[mutable_string->nx_string_data->len]=0; /* well null terminate */
+
+ return NErr_Success;
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxmutablestring.h b/Src/replicant/nx/win/nxmutablestring.h
new file mode 100644
index 000000000..494855822
--- /dev/null
+++ b/Src/replicant/nx/win/nxmutablestring.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "foundation/types.h"
+#include "nx/nxstring.h"
+#include "nx/nxapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct nx_mutable_string_struct_t
+{
+ size_t allocation_length;
+ nx_string_t nx_string_data;
+} nx_mutable_string_struct_t, *nx_mutable_string_t;
+
+
+NX_API nx_mutable_string_t NXMutableStringCreateFromXML(const nsxml_char_t *characters, size_t num_characters);
+NX_API void NXMutableStringDestroy(nx_mutable_string_t mutable_string);
+NX_API int NXMutableStringGrowFromXML(nx_mutable_string_t mutable_string, const nsxml_char_t *characters, size_t num_characters);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxonce.c b/Src/replicant/nx/win/nxonce.c
new file mode 100644
index 000000000..293534f72
--- /dev/null
+++ b/Src/replicant/nx/win/nxonce.c
@@ -0,0 +1,43 @@
+#include "nxonce.h"
+#include "foundation/error.h"
+#if 0 && _WIN32_WINNT >= 0x600
+
+void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *param)
+{
+ InitOnceExecuteOnce(once, (PINIT_ONCE_FN)init_fn, param, 0);
+}
+
+void NXOnceInit(nx_once_t once)
+{
+ InitOnceInitialize(once);
+}
+#else
+/* this ONLY works because of the strict(ish) memory ordering of the AMD64/x86 processors.
+ Don't use this implementation for a processor that has loose memory ordering restriction (e.g. ARM, PowerPC)
+ see http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
+ */
+void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *param)
+{
+ if (once->status)
+ return;
+
+ EnterCriticalSection(&once->critical_section);
+ if (once->status)
+ {
+ LeaveCriticalSection(&once->critical_section);
+ return;
+ }
+
+ init_fn(once, param, 0);
+ // benski> not important for the x86, but on processors with weak memory-order on stores, once->status might set to 1 BEFORE all stores from init_fn complete!
+ once->status = 1;
+ LeaveCriticalSection(&once->critical_section);
+}
+
+void NXOnceInit(nx_once_t once)
+{
+ once->status=0;
+ InitializeCriticalSection(&once->critical_section);
+}
+
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxonce.h b/Src/replicant/nx/win/nxonce.h
new file mode 100644
index 000000000..7cdd53a64
--- /dev/null
+++ b/Src/replicant/nx/win/nxonce.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "nxapi.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if 0 && _WIN32_WINNT >= 0x600
+ typedef INIT_ONCE nx_once_value_t;
+ typedef INIT_ONCE *nx_once_t;
+#define NX_ONCE_INITIALIZE INIT_ONCE_STATIC_INIT
+#define NX_ONCE_API CALLBACK
+
+NX_API void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *param);
+NX_API void NXOnceInit(nx_once_t once);
+#else
+ typedef struct nx_once_s
+ {
+ volatile int status;
+ CRITICAL_SECTION critical_section;
+ } nx_once_value_t, *nx_once_t;
+
+#define NX_ONCE_API
+
+ NX_API void NXOnce(nx_once_t once, int (NX_ONCE_API *init_fn)(nx_once_t, void *, void **), void *);
+ NX_API void NXOnceInit(nx_once_t once);
+
+#endif
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxpath.c b/Src/replicant/nx/win/nxpath.c
new file mode 100644
index 000000000..1841640b0
--- /dev/null
+++ b/Src/replicant/nx/win/nxpath.c
@@ -0,0 +1,113 @@
+#include "nxpath.h"
+#include <shlwapi.h>
+#include "foundation/error.h"
+
+static const wchar_t *FindExtension(nx_uri_t filename)
+{
+ size_t position;
+ if (!filename || !filename->string || !filename->len)
+ return 0;
+
+ position=filename->len;
+ while (position--)
+ {
+ wchar_t c = filename->string[position];
+ if (c == '.')
+ return &filename->string[position+1];
+ if (c == '/' || c == '\\')
+ return 0;
+ }
+ return 0;
+}
+
+static const wchar_t *FindFilename(nx_uri_t filename)
+{
+ size_t position;
+ if (!filename || !filename->string || !filename->len)
+ return 0;
+
+ position=filename->len;
+ while (position--)
+ {
+ wchar_t c = filename->string[position];
+ if (c == '/' || c == '\\')
+ return &filename->string[position+1];
+ }
+ return 0;
+}
+
+size_t NXPathMatchExtensionList(nx_uri_t filename, nx_string_t *extension_list, size_t num_extensions)
+{
+ const wchar_t *ext = FindExtension(filename);
+ if (ext && *ext)
+ {
+ size_t i;
+ for (i=0;i<num_extensions;i++)
+ {
+#if WINVER >= 0x0600
+ if (CompareStringOrdinal(ext, -1, extension_list[i]->string, -1, TRUE) == CSTR_EQUAL)
+ return i;
+#else
+ if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, ext, -1, extension_list[i]->string, -1) == CSTR_EQUAL)
+ return i;
+#endif
+ }
+ }
+ return num_extensions;
+}
+
+int NXPathMatchExtension(nx_uri_t filename, nx_string_t extension)
+{
+ const wchar_t *ext = FindExtension(filename);
+ if (ext && *ext)
+ {
+#if WINVER >= 0x0600
+ if (CompareStringOrdinal(ext, -1, extension->string, -1, TRUE) == CSTR_EQUAL)
+ return NErr_Success;
+#else
+ if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, ext, -1, extension->string, -1) == CSTR_EQUAL)
+ return NErr_Success;
+#endif
+ }
+ return NErr_False;
+}
+
+int NXPathProtocol(nx_uri_t filename, const char *protocol)
+{
+ if (PathIsURL(filename->string))
+ {
+ wchar_t protocol_from_filename[100];
+ DWORD protocol_length=100;
+ if (UrlGetPart(filename->string, protocol_from_filename, &protocol_length, URL_PART_SCHEME, 0) == S_OK)
+ {
+ DWORD i;
+ for (i=0;i<protocol_length;i++)
+ {
+ if ((wchar_t)(protocol[i]) != protocol_from_filename[i])
+ return NErr_False;
+ }
+ return NErr_Success;
+ }
+ }
+ return NErr_False;
+}
+
+int NXPathIsURL(nx_uri_t filename)
+{
+ if (PathIsURL(filename->string))
+ return NErr_True;
+ else
+ return NErr_False;
+}
+
+int NXPathIsRelative(nx_uri_t filename)
+{
+ if (filename->len >= 3 && filename->string[1] == L':' && (filename->string[2] == L'\\' || filename->string[2] == L'/'))
+ return NErr_False;
+
+ if (filename->len >= 1 && (filename->string[2] == L'\\' || filename->string[2] == L'/'))
+ return NErr_False;
+
+ return NErr_True;
+}
+
diff --git a/Src/replicant/nx/win/nxpath.h b/Src/replicant/nx/win/nxpath.h
new file mode 100644
index 000000000..baf70d715
--- /dev/null
+++ b/Src/replicant/nx/win/nxpath.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "foundation/types.h"
+#include "nx/nxapi.h"
+#include "nx/nxstring.h"
+#include "nx/nxuri.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // returns index into the extension list of a match extension
+ // returns >= num_extensions on failure
+NX_API size_t NXPathMatchExtensionList(nx_uri_t filename, nx_string_t *extension_list, size_t num_extensions);
+
+// return NErr_True / NErr_False
+NX_API int NXPathMatchExtension(nx_uri_t filename, nx_string_t extension);
+
+// return NErr_True / NErr_False
+NX_API int NXPathProtocol(nx_uri_t filename, const char *protocol);
+
+// return NErr_True / NErr_False
+NX_API int NXPathIsURL(nx_uri_t filename);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxsemaphore.c b/Src/replicant/nx/win/nxsemaphore.c
new file mode 100644
index 000000000..25070336a
--- /dev/null
+++ b/Src/replicant/nx/win/nxsemaphore.c
@@ -0,0 +1,24 @@
+#include "nxsemaphore.h"
+#include "foundation/error.h"
+int NXSemaphoreCreate(nx_semaphore_t *sem)
+{
+ *sem = CreateSemaphore(0, 0, LONG_MAX, 0);
+ return NErr_Success;
+}
+
+int NXSemaphoreRelease(nx_semaphore_t sem)
+{
+ ReleaseSemaphore(sem, 1, 0);
+ return NErr_Success;
+}
+
+int NXSemaphoreWait(nx_semaphore_t sem)
+{
+ WaitForSingleObject(sem, INFINITE);
+ return NErr_Success;
+}
+
+void NXSemaphoreClose(nx_semaphore_t sem)
+{
+ CloseHandle(sem);
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxsemaphore.h b/Src/replicant/nx/win/nxsemaphore.h
new file mode 100644
index 000000000..9509fe5a3
--- /dev/null
+++ b/Src/replicant/nx/win/nxsemaphore.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <windows.h>
+#include "nx/nxapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef HANDLE nx_semaphore_t;
+
+ NX_API int NXSemaphoreCreate(nx_semaphore_t *sem);
+ NX_API int NXSemaphoreRelease(nx_semaphore_t sem);
+ NX_API int NXSemaphoreWait(nx_semaphore_t sem);
+ NX_API void NXSemaphoreClose(nx_semaphore_t sem);
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxsleep.c b/Src/replicant/nx/win/nxsleep.c
new file mode 100644
index 000000000..245cf4c8a
--- /dev/null
+++ b/Src/replicant/nx/win/nxsleep.c
@@ -0,0 +1,14 @@
+#include "nxsleep.h"
+#include "foundation/error.h"
+int NXSleep(unsigned int milliseconds)
+{
+ Sleep(milliseconds);
+ return NErr_Success;
+}
+
+int NXSleepYield(void)
+{
+ Sleep(0);
+ return NErr_Success;
+}
+
diff --git a/Src/replicant/nx/win/nxsleep.h b/Src/replicant/nx/win/nxsleep.h
new file mode 100644
index 000000000..98da22f0f
--- /dev/null
+++ b/Src/replicant/nx/win/nxsleep.h
@@ -0,0 +1,17 @@
+#pragma once
+#include "nx/nxapi.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NX_API int NXSleep(unsigned int milliseconds);
+NX_API int NXSleepYield(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/nx/win/nxstring.c b/Src/replicant/nx/win/nxstring.c
new file mode 100644
index 000000000..fca479e2e
--- /dev/null
+++ b/Src/replicant/nx/win/nxstring.c
@@ -0,0 +1,756 @@
+#include "nxstring.h"
+#include "foundation/error.h"
+#include <shlwapi.h>
+#include "foundation/atomics.h"
+#include <wchar.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#pragma comment(lib, "shlwapi.lib")
+
+//#define NX_STRING_STRICT_HEAP
+
+HANDLE string_heap = 0;
+
+int NXStringSetHeap(HANDLE _string_heap)
+{
+ if (!string_heap)
+ {
+ string_heap = _string_heap;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_NoAction;
+ }
+}
+
+// don't include null terminator here
+static size_t NXStringMallocSize(size_t characters)
+{
+ /* TODO: overflow check? */
+ const nx_string_t dummy=NULL;
+ size_t header = (size_t)&dummy->string[0] - (size_t)dummy;
+ return header + (characters+1) * sizeof(wchar_t);
+}
+
+// don't include null terminator here
+nx_string_t NXStringMalloc(size_t characters)
+{
+ if (!string_heap)
+ {
+ string_heap = GetProcessHeap();
+ }
+ return NXStringMallocWithHeap(string_heap, characters);
+}
+
+nx_string_t NXStringRealloc(nx_string_t str, size_t characters)
+{
+ nx_string_t new_str = (nx_string_t)HeapReAlloc(string_heap, 0, str, NXStringMallocSize(characters));
+ // on failure, kick back the original block (TODO need to review this)
+ if (!new_str)
+ {
+ return str;
+ }
+ return new_str;
+}
+
+nx_string_t NXStringMallocWithHeap(HANDLE heap, size_t characters)
+{
+#ifdef NX_STRING_STRICT_HEAP
+ nx_string_t str;
+ size_t string_size = NXStringMallocSize(characters);
+
+ size_t allocated_size = (string_size + 8191) & ~4095;
+ size_t offset = 4096 - (string_size & 4095);
+ size_t pages = allocated_size / 4096;
+ uint8_t *protect_start;
+ void *mem = VirtualAlloc(0, allocated_size, MEM_COMMIT, PAGE_READWRITE);
+
+ if (!mem)
+ return 0;
+
+ protect_start = (uint8_t *)mem + (pages-1)*4096;
+ VirtualProtect(protect_start, 4096, PAGE_NOACCESS, 0);
+
+ str = (nx_string_t)((uint8_t *)mem + offset);
+ str->ref_count = 1;
+ str->len = characters;
+ return str;
+#else
+ nx_string_t str = (nx_string_t)HeapAlloc(heap, 0, NXStringMallocSize(characters));
+ if (str)
+ {
+ str->ref_count = 1;
+ str->len = characters;
+ }
+ return str;
+#endif
+}
+
+int NXStringFree(HANDLE heap, nx_string_t str)
+{
+#ifdef NX_STRING_STRICT_HEAP
+ uint8_t *mem = (uint8_t *)((size_t)str & 4095);
+ VirtualProtect(mem, 4096, PAGE_NOACCESS, 0);
+ assert(_heapchk() == _HEAPOK);
+ return NErr_Success;
+#else
+ if (HeapFree(heap, 0, str))
+ {
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+#endif
+}
+
+nx_string_t NXStringCreate(const wchar_t *str)
+{
+ size_t size;
+ nx_string_t nxstr;
+
+ if (!str || (size_t)str <= 65536)
+ {
+ return 0;
+ }
+
+ size = wcslen(str);
+ nxstr = NXStringMalloc(size);
+ if (nxstr)
+ {
+ memcpy(nxstr->string, str, size*sizeof(wchar_t));
+ nxstr->string[size]=0;
+ }
+ return nxstr;
+}
+
+int NXStringCreateEmpty(nx_string_t *new_string)
+{
+ nx_string_t nxstr = NXStringMalloc(0);
+ if (nxstr)
+ {
+ nxstr->string[0]=0;
+ *new_string = nxstr;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_OutOfMemory;
+ }
+}
+
+nx_string_t NXStringCreateWithHeap(HANDLE heap, const wchar_t *str)
+{
+ size_t size = wcslen(str);
+ nx_string_t nxstr = NXStringMallocWithHeap(heap, size);
+ if (nxstr)
+ {
+ memcpy(nxstr->string, str, size*sizeof(wchar_t));
+ nxstr->string[size]=0;
+ }
+ return nxstr;
+}
+
+nx_string_t NXStringCreateFromUTF8(const char *str)
+{
+ nx_string_t nxstr;
+ size_t size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0,0);
+ if (!size)
+ {
+ return 0;
+ }
+
+ nxstr = NXStringMalloc(size-1);
+ if (nxstr)
+ {
+ if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, nxstr->string, (int)size))
+ {
+ NXStringFree(string_heap, nxstr);
+ return 0;
+ }
+ }
+
+ return nxstr;
+}
+
+int NXStringCreateWithUTF8(nx_string_t *new_value, const char *str)
+{
+ size_t size;
+ nx_string_t nxstr;
+
+ if (!str)
+ {
+ return NErr_Empty;
+ }
+
+ size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0,0);
+ if (!size)
+ {
+ return NErr_Error;
+ }
+
+ nxstr = NXStringMalloc(size-1);
+ if (!nxstr)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ if (!MultiByteToWideChar(CP_UTF8, 0, str, -1, nxstr->string, (int)size))
+ {
+ NXStringFree(string_heap, nxstr);
+ return NErr_Error;
+ }
+
+ *new_value = nxstr;
+ return NErr_Success;
+}
+
+int NXStringCreateWithUTF16(nx_string_t *new_value, const wchar_t *str)
+{
+ size_t size;
+ nx_string_t nxstr;
+
+ if (!str)
+ {
+ return NErr_Empty;
+ }
+
+ size = wcslen(str);
+ nxstr = NXStringMalloc(size);
+ if (!nxstr)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ memcpy(nxstr->string, str, size*sizeof(wchar_t));
+ nxstr->string[size]=0;
+ *new_value = nxstr;
+ return NErr_Success;
+}
+
+int NXStringCreateWithCString(nx_string_t *new_value, const char *str, nx_charset_t charset)
+{
+ nx_string_t nxstr;
+ size_t size = MultiByteToWideChar(charset, 0, str, -1, 0,0);
+ if (!size)
+ {
+ return NErr_Error;
+ }
+
+ nxstr = NXStringMalloc(size-1);
+ if (!nxstr)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ if (!MultiByteToWideChar(charset, 0, str, -1, nxstr->string, (int)size))
+ {
+ NXStringFree(string_heap, nxstr);
+ return NErr_Error;
+ }
+
+ *new_value = nxstr;
+ return NErr_Success;
+}
+
+nx_string_t NXStringRetain(nx_string_t string)
+{
+ if (!string)
+ {
+ return 0;
+ }
+
+ nx_atomic_inc(&string->ref_count);
+ return string;
+}
+
+void NXStringRelease(nx_string_t string)
+{
+ if (string)
+ {
+ if (nx_atomic_dec(&string->ref_count) == 0)
+ {
+ NXStringFree(string_heap, string);
+ }
+ }
+}
+
+nx_string_t NXStringCreateFromPath(const wchar_t *folder, const wchar_t *filename)
+{
+ nx_string_t pathstr = NXStringMalloc(MAX_PATH);
+ if (pathstr)
+ {
+ PathCombineW(pathstr->string, folder, filename);
+ pathstr->len = wcslen(pathstr->string);
+ }
+ return pathstr;
+}
+
+nx_string_t NXStringCreateFromUInt64(uint64_t value)
+{
+ nx_string_t intstr = NXStringMalloc(21);
+ if (intstr)
+ {
+ _ui64tow(value, intstr->string, 10);
+ intstr->len = wcslen(intstr->string);
+ }
+ return intstr;
+}
+
+int NXStringCreateWithUInt64(nx_string_t *new_value, uint64_t value)
+{
+ nx_string_t intstr = NXStringMalloc(21);
+ if (!intstr)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ _ui64tow(value, intstr->string, 10);
+ intstr->len = wcslen(intstr->string);
+ *new_value = intstr;
+ return NErr_Success;
+}
+
+int NXStringCreateWithInt64(nx_string_t *new_value, int64_t value)
+{
+ nx_string_t intstr = NXStringMalloc(21);
+ if (!intstr)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ _i64tow(value, intstr->string, 10);
+ intstr->len = wcslen(intstr->string);
+ *new_value = intstr;
+ return NErr_Success;
+}
+
+int NXStringCreateWithBytes(nx_string_t *new_string, const void *data, size_t len, nx_charset_t charset)
+{
+ nx_string_t nxstr;
+ if (!len)
+ {
+ return NXStringCreateEmpty(new_string);
+ }
+ if (charset == nx_charset_utf16le)
+ {
+ nxstr = NXStringMalloc(len/2);
+ if (nxstr)
+ {
+ memcpy(nxstr->string, data, len);
+ nxstr->string[len/2]=0;
+ nxstr->len = len/2;
+ *new_string = nxstr;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_OutOfMemory;
+ }
+ }
+ else if (charset == nx_charset_utf16be)
+ {
+ nxstr = NXStringMalloc(len/2);
+ if (nxstr)
+ {
+ LCMapString(LOCALE_INVARIANT, LCMAP_BYTEREV, (LPCWSTR)data, (int)len/2, nxstr->string, (int)len/2);
+ nxstr->string[len/2]=0;
+ nxstr->len = len/2;
+ *new_string = nxstr;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_OutOfMemory;
+ }
+ }
+ else
+ {
+ int size = MultiByteToWideChar((UINT)charset, 0, (const char *)data, (int)len, 0, 0);
+ if (!size)
+ {
+ return NErr_Error;
+ }
+
+ nxstr = NXStringMalloc(size);
+ if (nxstr)
+ {
+ if (!MultiByteToWideChar((UINT)charset, 0, (const char *)data, (int)len, nxstr->string, size))
+ {
+ NXStringFree(string_heap, nxstr);
+ return NErr_Error;
+ }
+ nxstr->string[size]=0;
+ nxstr->len = size;
+ *new_string = nxstr;
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_OutOfMemory;
+ }
+ }
+}
+
+size_t NXStringGetLength(nx_string_t string)
+{
+ return (string ? string->len : 0);
+}
+
+/* --- Keyword (ASCII) comparison --- */
+int NXStringKeywordCompareWithCString(nx_string_t string, const char *compare_to)
+{
+ const wchar_t *src = string->string;
+ const char *dst = compare_to;
+
+ int ret = 0 ;
+
+ while( ! (ret = (int)((*src & ~0x20) - (*dst & ~0x20))) && *dst)
+ {
+ ++src, ++dst;
+ }
+
+ if ( ret < 0 )
+ {
+ ret = -1 ;
+ }
+ else if ( ret > 0 )
+ {
+ ret = 1 ;
+ }
+ return( ret );
+}
+
+int NXStringKeywordCompare(nx_string_t string, nx_string_t compare_to)
+{
+ const wchar_t *src = string->string;
+ const wchar_t *dst = compare_to->string;
+
+ int ret = 0 ;
+
+ while( ! (ret = (int)((*src & ~0x20) - (*dst & ~0x20))) && *dst)
+ {
+ ++src, ++dst;
+ }
+
+ if ( ret < 0 )
+ {
+ ret = -1 ;
+ }
+ else if ( ret > 0 )
+ {
+ ret = 1 ;
+ }
+ return( ret );
+}
+
+int NXStringKeywordCaseCompare(nx_string_t string, nx_string_t compare_to)
+{
+ const wchar_t *src = string->string;
+ const wchar_t *dst = compare_to->string;
+
+ int ret = 0 ;
+
+ while( ! (ret = (int)(*src - (wchar_t)*dst)) && *dst)
+ {
+ ++src, ++dst;
+ }
+
+ if ( ret < 0 )
+ {
+ ret = -1 ;
+ }
+ else if ( ret > 0 )
+ {
+ ret = 1 ;
+ }
+
+ return( ret );
+}
+
+int NXStringCreateBasePathFromFilename(nx_string_t filename, nx_string_t *basepath)
+{
+ nx_string_t nxstr;
+ size_t len = filename->len;
+ while (len && filename->string[len-1] != '\\' && filename->string[len-1] != '/')
+ {
+ len--;
+ }
+
+ if (!len)
+ {
+ return NErr_Empty;
+ }
+
+ nxstr = NXStringMalloc(len);
+ if (!nxstr)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ memcpy(nxstr->string, filename->string, sizeof(wchar_t)*len);
+ nxstr->string[len]=0;
+ *basepath = nxstr;
+ return NErr_Success;
+}
+
+int NXStringGetCString(nx_string_t string, char *user_buffer, size_t user_buffer_length, const char **out_cstring, size_t *out_cstring_length)
+{
+ size_t size;
+
+ /* TODO: error check this with large strings and small user_buffer_length sizes */
+ if (!string)
+ {
+ return NErr_NullPointer;
+ }
+
+ if (user_buffer_length == 0)
+ return NErr_Insufficient;
+
+ size = WideCharToMultiByte(CP_ACP, 0, string->string, (int)string->len, user_buffer, (int)user_buffer_length-1, NULL, NULL);
+ if (size == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ size = user_buffer_length-1;
+ user_buffer[size]=0;
+ *out_cstring = user_buffer;
+ *out_cstring_length = (size_t)size;
+ return NErr_Success;
+}
+
+int NXStringGetDoubleValue(nx_string_t string, double *value)
+{
+ if (!string)
+ return NErr_NullPointer;
+
+ *value = wcstod(string->string, 0);
+ return NErr_Success;
+}
+
+int NXStringGetBytesSize(size_t *byte_count, nx_string_t string, nx_charset_t charset, int flags)
+{
+ if (charset == nx_charset_utf16le)
+ {
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ *byte_count = (string->len + 1)* sizeof(wchar_t);
+ else
+ *byte_count = string->len * sizeof(wchar_t);
+ return NErr_DirectPointer;
+ }
+ else
+ {
+ size_t size=0;
+ /*if (flags & nx_string_get_bytes_size_null_terminate)
+ size = WideCharToMultiByte(charset, 0, string->string, string->len, 0, 0, NULL, NULL);
+ else*/
+ size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, 0, 0, NULL, NULL);
+
+ if (!size)
+ return NErr_Error;
+
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ *byte_count = size+1;
+ else
+ *byte_count = size;
+
+ return NErr_Success;
+ }
+}
+
+int NXStringGetBytesDirect(const void **bytes, size_t *length, nx_string_t string, nx_charset_t charset, int flags)
+{
+ if (charset == nx_charset_utf16le)
+ {
+ *bytes = string->string;
+ if (length)
+ {
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ *length = (string->len+1) * sizeof(wchar_t); // TODO: overflow check
+ else
+ *length = string->len * sizeof(wchar_t); // TODO: overflow check
+ }
+ return NErr_Success;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+
+}
+
+int NXStringGetBytes(size_t *bytes_copied, nx_string_t string, void *bytes, size_t length, nx_charset_t charset, int flags)
+{
+ if (charset == nx_charset_utf16le)
+ {
+ length/=2;
+
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ {
+ if (length == 0)
+ return NErr_Insufficient;
+
+ length--;
+ }
+
+ if (length > string->len)
+ length = string->len;
+ wmemcpy((wchar_t *)bytes, string->string, length);
+
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ ((wchar_t *)bytes)[length++]=0;
+
+ if (bytes_copied)
+ *bytes_copied = length * 2;
+ return NErr_Success;
+ }
+ else
+ {
+ size_t size=0;
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ {
+ size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, (LPSTR)bytes, (int)length-1, NULL, NULL);
+ ((char *)bytes)[size]=0;
+ }
+ else
+ {
+ size = WideCharToMultiByte(charset, 0, string->string, (int)string->len, (LPSTR)bytes, (int)length, NULL, NULL);
+ }
+
+ if (!size)
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ size = length-1;
+ else
+ size=length;
+ }
+ else
+ {
+ return NErr_Error;
+ }
+ }
+
+ if (bytes_copied)
+ {
+ if (flags & nx_string_get_bytes_size_null_terminate)
+ {
+ if (size)
+ *bytes_copied = size+1;
+ else
+ *bytes_copied = length+1;
+ }
+ else
+ {
+ if (size)
+ *bytes_copied = size;
+ else
+ *bytes_copied = length;
+ }
+ }
+ return NErr_Success;
+ }
+}
+
+int NXStringGetIntegerValue(nx_string_t string, int *value)
+{
+ *value = wcstol(string->string, 0, 10);
+ return NErr_Success;
+}
+
+int NXStringGetGUIDValue(nx_string_t string, GUID *out_guid)
+{
+ /* TODO: it'd be nice if this was a bit more flexible on input, e.g. no dashes vs dashes */
+ GUID guid = GUID_NULL;
+ size_t offset = 0;
+ int Data1, Data2, Data3;
+ int Data4[8] = {0};
+
+ for (;;)
+ {
+ if (string->string[offset] == '{')
+ {
+ offset++;
+ }
+ else if (string->string[offset] == ' ')
+ {
+ offset++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ //{ 0x1b3ca60c, 0xda98, 0x4826, { 0xb4, 0xa9, 0xd7, 0x97, 0x48, 0xa5, 0xfd, 0x73 } };
+ swscanf( string->string, L"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ &Data1, &Data2, &Data3, Data4 + 0, Data4 + 1,
+ Data4 + 2, Data4 + 3, Data4 + 4, Data4 + 5, Data4 + 6, Data4 + 7 );
+
+ // Cross assign all the values
+ guid.Data1 = Data1;
+ guid.Data2 = Data2;
+ guid.Data3 = Data3;
+ guid.Data4[0] = Data4[0];
+ guid.Data4[1] = Data4[1];
+ guid.Data4[2] = Data4[2];
+ guid.Data4[3] = Data4[3];
+ guid.Data4[4] = Data4[4];
+ guid.Data4[5] = Data4[5];
+ guid.Data4[6] = Data4[6];
+ guid.Data4[7] = Data4[7];
+
+ *out_guid = guid;
+ return NErr_Success;
+}
+
+nx_compare_result NXStringCompare(nx_string_t string1, nx_string_t string2, nx_compare_options options)
+{
+ int compareFlags = 0;
+
+ if (0 != (nx_compare_case_insensitive & options))
+ {
+ compareFlags |= NORM_IGNORECASE;
+ }
+ return CompareString(LOCALE_USER_DEFAULT, compareFlags, string1->string, -1, string2->string, -1) - 2;
+}
+
+int NXStringCreateWithFormatting(nx_string_t *new_string, const char *format, ...)
+{
+ size_t cch, ret;
+ char *temp = 0;
+ va_list v;
+ va_start(v, format);
+
+ cch = _vscprintf(format, v);
+ if (cch == -1)
+ {
+ return NErr_Error;
+ }
+
+ if (cch > 256)
+ {
+ temp = (char *)malloc(cch+1);
+ if (!temp)
+ {
+ return NErr_OutOfMemory;
+ }
+ vsprintf(temp, format, v);
+
+ ret = NXStringCreateWithUTF8(new_string, temp);
+ free(temp);
+ }
+ else
+ {
+ temp = (char *)_malloca(cch+1);
+ if (!temp)
+ {
+ return NErr_OutOfMemory;
+ }
+ vsprintf(temp, format, v);
+ ret = NXStringCreateWithUTF8(new_string, temp);
+ }
+ va_end(v);
+ return (int)ret;
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxstring.h b/Src/replicant/nx/win/nxstring.h
new file mode 100644
index 000000000..5e07777a1
--- /dev/null
+++ b/Src/replicant/nx/win/nxstring.h
@@ -0,0 +1,95 @@
+#pragma once
+#include "../../foundation/types.h"
+#include <windows.h>
+#include "../../nx/nxapi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef enum
+ {
+ nx_charset_ascii = 20127,
+ nx_charset_latin1 = 28591,
+ nx_charset_system = CP_ACP,
+ nx_charset_utf8 = CP_UTF8,
+ nx_charset_utf16le = 1200,
+ nx_charset_utf16be = 1201,
+ } nx_charset_t;
+
+ typedef struct nx_string_struct_t
+ {
+ size_t ref_count;
+ size_t len;
+ wchar_t string[1]; // utf-16
+ } nx_string_struct_t, *nx_string_t;
+
+ enum
+ {
+ nx_compare_less_than = -1,
+ nx_compare_equal_to = 0,
+ nx_compare_greater_than = 1,
+ };
+ typedef int nx_compare_result;
+
+ enum
+ {
+ nx_compare_default = 0,
+ nx_compare_case_insensitive = ( 1 << 0),
+ };
+ typedef unsigned long nx_compare_options;
+
+ NX_API int NXStringSetHeap(HANDLE string_heap);
+ NX_API nx_string_t NXStringCreateFromPath(const wchar_t *folder, const wchar_t *filename);
+ NX_API nx_string_t NXStringCreate(const wchar_t *str);
+ NX_API nx_string_t NXStringCreateWithHeap(HANDLE heap, const wchar_t *str);
+ NX_API nx_string_t NXStringMallocWithHeap(HANDLE heap, size_t characters);
+ NX_API nx_string_t NXStringMalloc(size_t characters);
+ NX_API nx_string_t NXStringRealloc(nx_string_t, size_t characters);
+
+ NX_API nx_string_t NXStringRetain(nx_string_t string);
+ NX_API void NXStringRelease(nx_string_t string);
+
+ NX_API nx_string_t NXStringCreateFromUTF8(const char *str);
+ NX_API int NXStringCreateWithUTF8(nx_string_t *new_value, const char *str);
+ NX_API int NXStringCreateWithUTF16(nx_string_t *new_value, const wchar_t *str);
+ NX_API int NXStringCreateWithCString(nx_string_t *new_value, const char *str, nx_charset_t charset);
+ NX_API nx_string_t NXStringCreateFromUInt64(uint64_t value);
+ NX_API int NXStringCreateWithUInt64(nx_string_t *new_value, uint64_t value);
+ NX_API int NXStringCreateWithInt64(nx_string_t *new_value, int64_t value);
+ NX_API int NXStringCreateWithBytes(nx_string_t *new_string, const void *data, size_t len, nx_charset_t charset);
+ NX_API int NXStringCreateEmpty(nx_string_t *new_string);
+ NX_API int NXStringCreateWithFormatting(nx_string_t *new_string, const char *format, ...);
+
+ NX_API size_t NXStringGetLength(nx_string_t string);
+
+ /* returns strcmp style return. compare_to is treated as an ASCII string.
+ if compare_to has non-ASCII characters, results are undetermined */
+ NX_API int NXStringKeywordCompareWithCString(nx_string_t string, const char *compare_to);
+ NX_API int NXStringKeywordCompare(nx_string_t string, nx_string_t compare_to);
+
+ NX_API int NXStringKeywordCaseCompare(nx_string_t string, nx_string_t compare_to);
+
+ /* creates an NXString with the base path from the passed in filename (retains an appended \) */
+ NX_API int NXStringCreateBasePathFromFilename(nx_string_t filename, nx_string_t *basepath);
+
+ NX_API int NXStringGetCString(nx_string_t string, char *user_buffer, size_t user_buffer_length, const char **out_cstring, size_t *out_cstring_length);
+ NX_API int NXStringGetDoubleValue(nx_string_t string, double *value);
+ NX_API int NXStringGetIntegerValue(nx_string_t string, int *value);
+ NX_API int NXStringGetGUIDValue(nx_string_t string, GUID *guid);
+
+ static const int nx_string_get_bytes_size_null_terminate = 1; // pass this to null terminate the string
+ /* returns byte count with enough room to store a converted string
+ note: if this returns NErr_DirectPointer, you can call NXStringGetBytesDirect to directly retrieve a pointer. */
+ NX_API int NXStringGetBytesSize(size_t *byte_count, nx_string_t string, nx_charset_t charset, int flags);
+ /* if possible, retrieves a pointer to bytes.
+ the length returned depends on whether or not you passed nx_string_get_bytes_size_null_terminate
+ note: the pointer you get will be invalid after you call NXStringRelease on the string passed in */
+ NX_API int NXStringGetBytesDirect(const void **bytes, size_t *length, nx_string_t string, nx_charset_t charset, int flags);
+ NX_API int NXStringGetBytes(size_t *bytes_copied, nx_string_t string, void *bytes, size_t length, nx_charset_t charset, int flags);
+
+ NX_API nx_compare_result NXStringCompare(nx_string_t string1, nx_string_t string2, nx_compare_options options);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxthread.c b/Src/replicant/nx/win/nxthread.c
new file mode 100644
index 000000000..0c049ad49
--- /dev/null
+++ b/Src/replicant/nx/win/nxthread.c
@@ -0,0 +1,25 @@
+#include "nxthread.h"
+#include "foundation/error.h"
+
+int NXThreadCreate(nx_thread_t *thread, nx_thread_func_t thread_function, nx_thread_parameter_t parameter)
+{
+ *thread = (nx_thread_t)CreateThread(0, 0, thread_function, parameter, 0, 0);
+ return NErr_Success;
+}
+
+int NXThreadJoin(nx_thread_t t, nx_thread_return_t *retval)
+{
+ if (!t)
+ return NErr_NullPointer;
+ WaitForSingleObject((HANDLE)t, INFINITE);
+ if (retval)
+ GetExitCodeThread((HANDLE)t, retval);
+ CloseHandle((HANDLE)t);
+ return NErr_Success;
+}
+
+int NXThreadCurrentSetPriority(int priority)
+{
+ SetThreadPriority(GetCurrentThread(), priority);
+ return NErr_Success;
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxthread.h b/Src/replicant/nx/win/nxthread.h
new file mode 100644
index 000000000..5b2c25616
--- /dev/null
+++ b/Src/replicant/nx/win/nxthread.h
@@ -0,0 +1,32 @@
+
+#pragma once
+#include "nx/nxapi.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct nx_thread_struct_t { size_t dummy; } *nx_thread_t;
+ typedef DWORD nx_thread_return_t;
+ typedef void *nx_thread_parameter_t;
+#define NXTHREADCALL CALLBACK
+ typedef nx_thread_return_t (NXTHREADCALL *nx_thread_func_t)(nx_thread_parameter_t parameter);
+
+ // TODO: add parameters for things like stack size
+NX_API int NXThreadCreate(nx_thread_t *thread, nx_thread_func_t thread_function, nx_thread_parameter_t parameter);
+NX_API int NXThreadJoin(nx_thread_t t, nx_thread_return_t *retval);
+
+enum
+{
+ NX_THREAD_PRIORITY_PLAYBACK=THREAD_PRIORITY_HIGHEST,
+};
+// sets priority of current thread
+NX_API int NXThreadCurrentSetPriority(int priority);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxtime.h b/Src/replicant/nx/win/nxtime.h
new file mode 100644
index 000000000..dad71c6b0
--- /dev/null
+++ b/Src/replicant/nx/win/nxtime.h
@@ -0,0 +1,10 @@
+#pragma once
+#include "../../foundation/types.h"
+#include "../../nx/nxapi.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef uint64_t nx_time_unix_64_t;
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxuri.c b/Src/replicant/nx/win/nxuri.c
new file mode 100644
index 000000000..afe99a215
--- /dev/null
+++ b/Src/replicant/nx/win/nxuri.c
@@ -0,0 +1,258 @@
+#include "nxuri.h"
+#include <stdlib.h>
+#include "foundation/atomics.h"
+#include "foundation/error.h"
+#include "nxstring.h" // for string_heap
+#include "foundation/atomics.h"
+
+HANDLE string_heap;
+
+int NXStringFree(HANDLE heap, nx_string_t str);
+
+nx_uri_t NXURIRetain(nx_uri_t string)
+{
+ if (!string)
+ {
+ return 0;
+ }
+
+ nx_atomic_inc(&string->ref_count);
+ return string;
+}
+
+void NXURIRelease(nx_uri_t string)
+{
+ if (string)
+ {
+ if (nx_atomic_dec(&string->ref_count) == 0)
+ {
+ NXStringFree(string_heap, (nx_string_t)string);
+ }
+ }
+}
+
+// don't include null terminator here
+nx_uri_t NXURIMalloc(size_t characters)
+{
+ return (nx_uri_t)NXStringMalloc(characters);
+}
+
+int NXURICreateWithNXString(nx_uri_t *uri, nx_string_t string)
+{
+ if (!string)
+ {
+ return NErr_Empty;
+ }
+
+ *uri = NXURIRetain((nx_uri_t)string);
+ return NErr_Success;
+}
+
+int NXURICreateFromPath(nx_uri_t *uri, const wchar_t *filename, const nx_uri_t path)
+{
+ size_t filename_length = wcslen(filename);
+ size_t path_length = path->len;
+ size_t total_length = filename_length + path_length; /* TODO: check for overflow */
+ int need_slash = 1;
+ nx_uri_t output=0;
+ if (path_length && (path->string[path_length-1] == '/' || path->string[path_length-1] == '\\'))
+ {
+ need_slash=0;
+ }
+ else
+ {
+ total_length++; /* TODO: check for overflow */
+ }
+
+ output = NXURIMalloc(total_length);
+ if (!output)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ wmemcpy(output->string, path->string, path_length);
+ if (need_slash)
+ {
+ output->string[path_length]='\\'; /* TODO: URL detection to know whether to add / or \\ */
+ wcscpy(&output->string[path_length+1], filename);
+ }
+ else
+ {
+ wcscpy(&output->string[path_length], filename);
+ }
+
+ *uri = output;
+ return NErr_Success;
+}
+
+int NXURICreateWithPath(nx_uri_t *uri, const nx_uri_t filename, const nx_uri_t path)
+{
+ size_t filename_length = filename->len;
+ size_t path_length = path->len;
+ size_t total_length = filename_length + path_length; /* TODO: check for overflow */
+ int need_slash = 1;
+ nx_uri_t output=0;
+ if (path_length && (path->string[path_length-1] == '/' || path->string[path_length-1] == '\\'))
+ {
+ need_slash=0;
+ }
+ else
+ {
+ total_length++; /* TODO: check for overflow */
+ }
+
+ output = NXURIMalloc(total_length);
+ if (!output)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ wmemcpy(output->string, path->string, path_length);
+ if (need_slash)
+ {
+ output->string[path_length]='\\'; /* TODO: URL detection to know whether to add / or \\ */
+ wcscpy(&output->string[path_length+1], filename->string);
+ }
+ else
+ {
+ wcscpy(&output->string[path_length], filename->string);
+ }
+
+ *uri = output;
+ return NErr_Success;
+}
+
+int NXURIGetNXString(nx_string_t *string, nx_uri_t uri)
+{
+ *string = (nx_string_t)NXURIRetain(uri);
+ return NErr_Success;
+}
+
+static const wchar_t *FindFilename(nx_uri_t filename)
+{
+ size_t position;
+ if (!filename || !filename->string || !filename->len)
+ {
+ return 0;
+ }
+
+ position=filename->len;
+ while (position--)
+ {
+ wchar_t c = filename->string[position];
+ if (c == '/' || c == '\\')
+ {
+ return &filename->string[position+1];
+ }
+ }
+ return 0;
+}
+
+int NXURICreateTempForFilepath(nx_uri_t *out_temp, nx_uri_t filename)
+{
+ nx_uri_t new_uri;
+ size_t path_length = 0;
+ wchar_t temp_part[64] = {0};
+#if _WIN32_WINNT >= 0x600
+ int temp_length = wsprintf(temp_part, L".%x-%I64x-%d.tmp", GetCurrentThreadId(), GetTickCount64(), rand());
+#else
+ int temp_length = wsprintf(temp_part, L".%x-%Ix-%d.tmp", GetCurrentThreadId(), GetTickCount(), rand());
+#endif
+ const wchar_t *filepart = FindFilename(filename);
+ if (filepart)
+ {
+ path_length = (filepart - filename->string);
+ }
+ else
+ {
+ path_length=0;
+ }
+ new_uri = NXURIMalloc(path_length+temp_length);
+ if (!new_uri)
+ {
+ return NErr_OutOfMemory;
+ }
+ wmemcpy(new_uri->string, filename->string, path_length);
+ wmemcpy(new_uri->string+path_length, temp_part, temp_length);
+ new_uri->string[path_length+temp_length]=0;
+ *out_temp = new_uri;
+ return NErr_Success;
+}
+
+int NXURICreateWithUTF8(nx_uri_t *value, const char *utf8)
+{
+ nx_string_t nx_filename;
+ nx_uri_t uri_filename;
+
+ int ret = NXStringCreateWithUTF8(&nx_filename, utf8);
+ if (ret != NErr_Success)
+ {
+ return ret;
+ }
+
+ ret = NXURICreateWithNXString(&uri_filename, nx_filename);
+ NXStringRelease(nx_filename);
+ if (ret != NErr_Success)
+ return ret;
+
+ *value = uri_filename;
+ return NErr_Success;
+}
+
+int NXURICreateRemovingFilename(nx_uri_t *out_uri, nx_uri_t filename)
+{
+ nx_uri_t new_uri;
+ size_t path_length;
+
+ const wchar_t *filepart = FindFilename(filename);
+ if (filepart)
+ {
+ path_length = (filepart - filename->string);
+ }
+ else
+ {
+ path_length=0;
+ }
+ new_uri = NXURIMalloc(path_length);
+ if (!new_uri)
+ {
+ return NErr_OutOfMemory;
+ }
+ wmemcpy(new_uri->string, filename->string, path_length);
+ new_uri->string[path_length]=0;
+ *out_uri = new_uri;
+ return NErr_Success;
+}
+
+int NXURICreateTemp(nx_uri_t *out_temp)
+{
+ return NXURICreateTempWithExtension(out_temp, "tmp");
+}
+
+int NXURICreateTempWithExtension(nx_uri_t *out_temp, const char *extension)
+{
+ nx_uri_t new_uri;
+ wchar_t temppath[MAX_PATH-14] = {0}; // MAX_PATH-14 'cause MSDN said so
+ int path_length = GetTempPathW(MAX_PATH-14, temppath);
+ wchar_t temp_part[64] = {0};
+#if _WIN32_WINNT >= 0x600
+ int temp_length = wsprintf(temp_part, L".%x-%I64x-%d.%S", GetCurrentThreadId(), GetTickCount64(), rand(), extension);
+#else
+ int temp_length = wsprintf(temp_part, L".%x-%Ix-%d.%S", GetCurrentThreadId(), GetTickCount(), rand(), extension);
+#endif
+ new_uri = NXURIMalloc(path_length+temp_length);
+ if (!new_uri)
+ {
+ return NErr_OutOfMemory;
+ }
+ wmemcpy(new_uri->string, temppath, path_length);
+ wmemcpy(new_uri->string+path_length, temp_part, temp_length);
+ new_uri->string[path_length+temp_length]=0;
+ *out_temp = new_uri;
+ return NErr_Success;
+}
+
+size_t NXURIGetLength(nx_uri_t string)
+{
+ return (string ? string->len : 0);
+} \ No newline at end of file
diff --git a/Src/replicant/nx/win/nxuri.h b/Src/replicant/nx/win/nxuri.h
new file mode 100644
index 000000000..c4a7bd1ae
--- /dev/null
+++ b/Src/replicant/nx/win/nxuri.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "../../foundation/types.h"
+#include "../../nx/nxapi.h"
+#include "../../nx/nxstring.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+ /* this thing is meant to be identical to nx_string_t, but we want to try to protect it so they don't get easily cast to nx_string_t
+ since they aren't the same on all platforms */
+ typedef struct nx_uri_struct_t
+ {
+ size_t ref_count;
+ size_t len;
+ wchar_t string[1]; // utf-16
+ } nx_uri_struct_t, *nx_uri_t;
+
+ NX_API nx_uri_t NXURIRetain(nx_uri_t string);
+ NX_API void NXURIRelease(nx_uri_t string);
+
+ NX_API nx_uri_t NXURIMalloc(size_t characters);
+ NX_API int NXURICreateWithNXString(nx_uri_t *uri, nx_string_t string);
+ NX_API int NXURICreateFromPath(nx_uri_t *uri, const wchar_t *filename, const nx_uri_t path); // windows only
+ NX_API int NXURICreateWithPath(nx_uri_t *uri, const nx_uri_t filename, const nx_uri_t path);
+ NX_API int NXURICreateWithNXString(nx_uri_t *new_value, nx_string_t string);
+ NX_API int NXURICreateTempForFilepath(nx_uri_t *out_temp, nx_uri_t filename);
+ NX_API int NXURICreateTempWithExtension(nx_uri_t *out_temp, const char *extension);
+ NX_API int NXURICreateWithUTF8(nx_uri_t *value, const char *utf8);
+ NX_API int NXURICreateTemp(nx_uri_t *out_temp);
+
+ // replaces only the filename portion of the path with the desired filename
+ // e.g., filepath = /path/to/1.mp3, *out_uri = /path/to/
+ NX_API int NXURICreateRemovingFilename(nx_uri_t *out_uri, nx_uri_t filename);
+
+
+ NX_API int NXURIGetNXString(nx_string_t *string, nx_uri_t uri);
+
+ NX_API size_t NXURIGetLength(nx_uri_t string);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/replicant/ogg/api.h b/Src/replicant/ogg/api.h
new file mode 100644
index 000000000..16d89ca88
--- /dev/null
+++ b/Src/replicant/ogg/api.h
@@ -0,0 +1,8 @@
+#pragma once
+#include "../replicant/service/api_service.h"
+extern api_service *serviceManager;
+#define WASABI2_API_SVC serviceManager
+
+#include "../replicant/application/api_application.h"
+extern api_application *applicationApi;
+#define WASABI2_API_APP applicationApi
diff --git a/Src/replicant/ogg/ifc_oggaudiodecoder.h b/Src/replicant/ogg/ifc_oggaudiodecoder.h
new file mode 100644
index 000000000..cf228162b
--- /dev/null
+++ b/Src/replicant/ogg/ifc_oggaudiodecoder.h
@@ -0,0 +1,16 @@
+#pragma once
+#include "../replicant/foundation/dispatch.h"
+
+class ifc_oggaudiodecoder : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_oggaudiodecoder() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_oggaudiodecoder() {}
+public:
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ GET_BPS
+ };
+}; \ No newline at end of file
diff --git a/Src/replicant/ogg/svc_oggdecoder.h b/Src/replicant/ogg/svc_oggdecoder.h
new file mode 100644
index 000000000..9c227fe3a
--- /dev/null
+++ b/Src/replicant/ogg/svc_oggdecoder.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "../replicant/foundation/dispatch.h"
+#include "ogg/ogg.h"
+#include "../replicant/ogg/ifc_oggaudiodecoder.h"
+#include "../replicant/foundation/error.h"
+
+// {EC953F63-DFD3-41E7-B893-92537AE88280}
+static const GUID ogg_decoder_service_type_guid =
+{ 0xec953f63, 0xdfd3, 0x41e7, { 0xb8, 0x93, 0x92, 0x53, 0x7a, 0xe8, 0x82, 0x80 } };
+
+class svc_oggdecoder : public Wasabi2::Dispatchable
+{
+protected:
+ svc_oggdecoder() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_oggdecoder() {}
+public:
+ static GUID GetServiceType() { return ogg_decoder_service_type_guid; }
+ ns_error_t CreateAudioDecoder(ogg_packet *packet, ifc_oggaudiodecoder **audio_decoder) { return OggDecoder_CreateAudioDecoder(packet, audio_decoder); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+private:
+ virtual ns_error_t WASABICALL OggDecoder_CreateAudioDecoder(ogg_packet *packet, ifc_oggaudiodecoder **audio_decoder)=0;
+};
diff --git a/Src/replicant/player/ifc_playback.h b/Src/replicant/player/ifc_playback.h
new file mode 100644
index 000000000..e208dd5ac
--- /dev/null
+++ b/Src/replicant/player/ifc_playback.h
@@ -0,0 +1,71 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "types.h"
+#include "svc_output.h"
+#include "ifc_playback_parameters.h"
+
+/* Note that since a typical ifc_playback implementation
+is running on another thread, most functions will always succeed (except for out of memory or thread creation problems)
+but might report an error later to the ifc_player object that was passed
+when creating the playback object */
+
+class NOVTABLE ifc_playback : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_playback() : Dispatchable(NUM_DISPATCH_CODES) {}
+ ~ifc_playback() {}
+public:
+ /* optionally call Cue before calling Play to set the start and end positions
+ in a cuesheet situation, these will might also get called after
+ you indicate that cue end position has been reached */
+ int Cue(Agave_PositionType position_type, Agave_Position start, Agave_Position end) { return Playback_Cue(position_type, start, end); }
+ // start only version of Cue
+ int CueStart(Agave_PositionType position_type, Agave_Position start) { return Playback_CueStart(position_type, start); }
+
+ // begins playback.
+ int Play(svc_output *output, ifc_playback_parameters *secondary_parameters) { return Playback_Play(output, secondary_parameters); }
+
+ int SeekSeconds(double seconds) { return Playback_SeekSeconds(seconds); }
+
+ // called on user-initiated stop. not called if the playback object indiciated a stop (e.g. EOF)
+ int Stop() { return Playback_Stop(); }
+
+ int Pause() { return Playback_Pause(); }
+ int Unpause() { return Playback_Unpause(); }
+
+ /* called to shut things down.
+ note that if your playback object is in a 'stopped' state,
+ it should be prepared to receive Cue/Play call until Close() is called */
+ int Close() { return Playback_Close(); }
+
+ /* most of the time, you'll pass SetVolume and SetPan on to the output object
+ but some special-use playback objects (e.g. analog CD playback) might have to implement this */
+ // 0 to 1.0
+ int SetVolume(float volume) { return Playback_SetVolume(volume); }
+ // -1.0 to 1.0
+ int SetPan(float pan) { return Playback_SetPan(pan); }
+
+ /* most of the time, you'll ignore SetEQ
+ but some special-use playback objects (e.g. analog CD playback) might have to implement this */
+ int SetEQ(float preamp, int num_bands, float *bands) { return Playback_SetEQ(preamp, num_bands, bands); }
+
+ enum
+ {
+ NUM_DISPATCH_CODES,
+ };
+
+protected:
+ virtual int WASABICALL Playback_Cue(Agave_PositionType position_type, Agave_Position start, Agave_Position end) { return NErr_NotImplemented; }
+ virtual int WASABICALL Playback_CueStart(Agave_PositionType position_type, Agave_Position start) { return NErr_NotImplemented; }
+ virtual int WASABICALL Playback_Play(svc_output *output, ifc_playback_parameters *secondary_parameters)=0;
+ virtual int WASABICALL Playback_SeekSeconds(double seconds)=0;
+ virtual int WASABICALL Playback_Stop()=0;
+ virtual int WASABICALL Playback_Pause()=0;
+ virtual int WASABICALL Playback_Unpause()=0;
+ virtual int WASABICALL Playback_Close()=0;
+ virtual int WASABICALL Playback_SetVolume(float volume) { return NErr_NotImplemented; }
+ virtual int WASABICALL Playback_SetPan(float pan) { return NErr_NotImplemented; }
+ virtual int WASABICALL Playback_SetEQ(float preamp, int num_bands, float *bands) { return NErr_NotImplemented; }
+};
+
diff --git a/Src/replicant/player/ifc_playback_parameters.h b/Src/replicant/player/ifc_playback_parameters.h
new file mode 100644
index 000000000..1324b7d64
--- /dev/null
+++ b/Src/replicant/player/ifc_playback_parameters.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+/* ifc_output_parameters abstracts output parameters that are passed to an input plugin
+ it is things that an input plugin wouldn't necessary know about
+ for example, is a playback object is being used for track preview,
+ it might be configured to play out of a different output device
+ and there's no way an input plugin would know that
+ */
+class NOVTABLE ifc_playback_parameters : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_playback_parameters() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_playback_parameters() {}
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+};
diff --git a/Src/replicant/player/ifc_player.h b/Src/replicant/player/ifc_player.h
new file mode 100644
index 000000000..368d8f74c
--- /dev/null
+++ b/Src/replicant/player/ifc_player.h
@@ -0,0 +1,93 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "foundation/types.h"
+#include "metadata/ifc_metadata.h"
+#include "audio/ifc_equalizer.h"
+#include "nx/nxuri.h"
+
+/* implemented by Winamp (or whatever application)
+your ifc_playback implementation should call this with events
+
+TODO: benski> should we require the ifc_playback object to get passed in to each function?
+*/
+
+class NOVTABLE ifc_player : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_player() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_player() {}
+public:
+
+ /* When your playback object has read enough of the file to provide metadata
+ it should call this method.
+ It will be called for both song metadata (artist, album, etc) and codec metadata (bitrate, samplerate, etc).
+ so make sure you can provide both - this might mean you have to wait until the first frame is decoded.
+ The player object will add a reference in the function, and release whenever it is no longer needed (usually on start of next track)
+ */
+ void SetMetadata(ifc_metadata *metadata) { Player_SetMetadata(metadata); }
+
+ /* Call this just once (with timestamp=0) for CBR files or update continuously for VBR
+ bitrate should be in bits per second, e.g. 128000 (not 128!)
+ */
+ void SetBitrate(uint64_t bitrate, double timestamp) { Player_SetBitrate(bitrate, timestamp); }
+
+ /* Playback plugin should call this when it knows the length
+ can be updated if necessary (e.g. for VBR files w/o header)
+ If this is not called, it assumed to be a stream (radio)
+ */
+ void SetLength(double length) { Player_SetLength(length); }
+
+ /* Output plugin should call this every once in a while,
+ if your input plugin does its own audio output (e.g. analog CD) you should call this yourself
+ */
+ void SetPosition(double timestamp) { Player_SetPosition(timestamp); }
+
+ void OnLoaded(nx_uri_t filename) { Player_OnLoaded(filename); }
+ /* Input plugin should call this when playback ends at the end of the file
+ Do not call if playback stopped because of an error */
+ void OnEndOfFile() { Player_OnEndOfFile(); }
+
+ void OnError(NError code) { Player_OnError(code); }
+
+ void OnStopped() { Player_OnStopped(); }
+
+ void SetEqualizer(ifc_equalizer *equalizer) { return Player_SetEqualizer(equalizer); }
+
+ /* percent is 0-100. setting to 100 implies that buffering has finished */
+ void SetBufferStatus(int percent) { return Player_SetBufferStatus(percent); }
+
+ void OnSeekComplete(int error_code, double new_position) { return Player_OnSeekComplete(error_code, new_position); }
+
+ /* seekable is 0 (false) or 1 (true) */
+ void SetSeekable(int seekable) { return Player_SetSeekable(seekable); }
+
+ void AsynchronousFunctionCall(void (*function)(void *, void *, double), void *param1, void *param2, double real_param) { Player_AsynchronousFunctionCall(function, param1, param2, real_param); }
+
+ /* Call this after you've successfully opened and parsed the playback file, and are attempting to start playback */
+ void OnReady() { Player_OnReady(); };
+
+ /* Call this after EndOfFile() when either 1) You process an ifc_playback::Close() call or 2) ifc_audioout::IsPlaying() returns NErr_False while waiting for a Close() call */
+ void OnClosed() { Player_OnClosed(); };
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual void WASABICALL Player_SetMetadata(ifc_metadata *metadata) = 0;
+ virtual void WASABICALL Player_SetBitrate(uint64_t bitrate, double timestamp) = 0;
+ virtual void WASABICALL Player_SetLength(double length) = 0;
+ virtual void WASABICALL Player_SetPosition(double timestamp) = 0;
+ virtual void WASABICALL Player_OnLoaded(nx_uri_t filename) = 0;
+ virtual void WASABICALL Player_OnEndOfFile() = 0;
+ virtual void WASABICALL Player_OnError(NError code) = 0;
+ virtual void WASABICALL Player_OnStopped()=0;
+ virtual void WASABICALL Player_SetEqualizer(ifc_equalizer *equalizer)=0;
+ virtual void WASABICALL Player_SetBufferStatus(int percent)=0;
+ virtual void WASABICALL Player_OnSeekComplete(int error_code, double new_position)=0;
+ virtual void WASABICALL Player_SetSeekable(int seekable)=0;
+ virtual void WASABICALL Player_AsynchronousFunctionCall(void (*function)(void *, void *, double), void *param1, void *param2, double real_param)=0;
+ virtual void WASABICALL Player_OnReady()=0;
+ virtual void WASABICALL Player_OnClosed()=0;
+};
diff --git a/Src/replicant/player/svc_output.h b/Src/replicant/player/svc_output.h
new file mode 100644
index 000000000..264918a3d
--- /dev/null
+++ b/Src/replicant/player/svc_output.h
@@ -0,0 +1,40 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "audio/ifc_audioout.h"
+#include "player/ifc_playback_parameters.h"
+
+class ifc_player;
+
+// {FB5E9AE3-E033-407C-942B-6C1BFAF52A5C}
+static const GUID output_service_guid =
+{ 0xfb5e9ae3, 0xe033, 0x407c, { 0x94, 0x2b, 0x6c, 0x1b, 0xfa, 0xf5, 0x2a, 0x5c } };
+
+class NOVTABLE svc_output : public Wasabi2::Dispatchable
+{
+protected:
+ svc_output() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_output() {}
+public:
+ static GUID GetServiceType() { return output_service_guid; }
+ /* ----- Audio Output ----- */
+ // Opens winamp2-style Audio Output - good for audio-only streams (buffered, push, output-plugin-defined buffersize)
+ int AudioOpen(const ifc_audioout::Parameters *format, ifc_player *player, ifc_playback_parameters *secondary_parameters, ifc_audioout **out_output) { return OutputService_AudioOpen(format, player, secondary_parameters, out_output); }
+
+ /* ----- Video Output ----- */
+ int VideoOpen();
+
+ /* ----- Text Output ----- */
+ // Opens a subtitle stream
+ int TextOpenSubtitle();
+ // Opens a video info text stream
+ int TextOpenInfo();
+ // Opens a lyrics text stream
+ int TextOpenLyrics();
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual int WASABICALL OutputService_AudioOpen(const ifc_audioout::Parameters *format, ifc_player *player, ifc_playback_parameters *secondary_parameters, ifc_audioout **out_output) = 0;
+};
diff --git a/Src/replicant/player/types.h b/Src/replicant/player/types.h
new file mode 100644
index 000000000..62a07104f
--- /dev/null
+++ b/Src/replicant/player/types.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "foundation/types.h"
+
+enum Agave_PositionType
+{
+ AGAVE_PLAYPOSITION_100NANOECONDS = 0,
+ AGAVE_PLAYPOSITION_MILLISECONDS = 1,
+ AGAVE_PLAYPOSITION_SECONDS = 2,
+ AGAVE_PLAYPOSITION_HMSF= 3,
+ AGAVE_PLAYPOSITION_SAMPLE_FRAMES = 4,
+ AGAVE_PLAYPOSITION_BYTES = 5,
+ AGAVE_PLAYPOSITION_PACKETS = 6,
+};
+
+struct Agave_HMSF
+{
+ uint8_t hours;
+ uint8_t minutes;
+ uint8_t seconds;
+ uint8_t frames;
+};
+
+union Agave_Position
+{
+ uint64_t nanoseconds100; // in increments of 100 nanoseconds (microsoft style)
+ uint64_t milliseconds;
+ double seconds;
+ Agave_HMSF hmsf;
+ uint64_t sample_frames;
+ uint64_t bytes;
+ uint64_t packets;
+};
+
+struct Agave_Seek
+{
+ Agave_PositionType position_type;
+ Agave_Position position;
+};
+
+static void Agave_Seek_SetBytes(Agave_Seek *seek, uint64_t bytes)
+{
+ seek->position_type=AGAVE_PLAYPOSITION_BYTES;
+ seek->position.bytes = bytes;
+}
diff --git a/Src/replicant/replaygain/ifc_replaygain_settings.h b/Src/replicant/replaygain/ifc_replaygain_settings.h
new file mode 100644
index 000000000..5413dcb4d
--- /dev/null
+++ b/Src/replicant/replaygain/ifc_replaygain_settings.h
@@ -0,0 +1,62 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+/* ok, this could have easily been a config group, but I decided to make it its own interface for two reasons
+1) it's a little more maintainable and easier to understand and use, rather than just using a generic key/value interface
+2) the original ReplayGain specs suggests using the average gain of the last 10 played tracks in cases of missing replay gain data
+http://replaygain.hydrogenaudio.org/proposal/player_scale.html
+So we need a way to have the playback object pass along gain values so they can be averages in this way
+
+*/
+
+// {1CE24DEC-A189-4BC7-86A7-C6CDB0F8953D}
+static const GUID replaygain_settings_interface_guid =
+{ 0x1ce24dec, 0xa189, 0x4bc7, { 0x86, 0xa7, 0xc6, 0xcd, 0xb0, 0xf8, 0x95, 0x3d } };
+
+class ifc_metadata;
+
+class ifc_replaygain_settings : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_replaygain_settings() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_replaygain_settings() {}
+public:
+ static GUID GetInterfaceGUID() { return replaygain_settings_interface_guid; }
+
+ enum
+ {
+ REPLAYGAIN_OFF=0x0,
+ REPLAYGAIN_ON=0x1,
+ REPLAYGAIN_MODE_TRACK=0x2,
+ REPLAYGAIN_MODE_ALBUM=0x4,
+ REPLAYGAIN_MODE_MASK=REPLAYGAIN_MODE_TRACK|REPLAYGAIN_MODE_ALBUM,
+ REPLAYGAIN_PREVENT_CLIPPING=0x8,
+ REPLAYGAIN_AUTO=0x16, // automatically determine gain values for unscanned tracks from prior history
+ };
+
+ /* pass the ifc_metadata associated with the track
+ warning values:
+ NErr_Success: gain is set to the gain adjustment to play this track with
+ NErr_False: gain is set to the default gain adjustment because there was no metadata
+ NErr_Unknown: gain is set to the default gain adjustment, because the metadata object does not understand the ReplayGain metadata keys (MetadataKeys::TRACK_GAIN, etc)
+
+ return values:
+ NErr_Success: *gain has been assigned and is valid
+ NErr_Disabled: ReplayGain is turned off
+ */
+ int GetGain(ifc_metadata *metadata, double *gain, int *warning) { return ReplayGainSettings_GetGain(metadata, gain, warning); }
+
+ /* Adds a track to the history, which is optionally used when playing back tracks without ReplayGain
+ pass the track length (or an estimate). Pass 0.0 for length if don't know.
+ You should only call this if GetGain returned NErr_Success and there was no warning!
+ */
+ int AddToHistory(double seconds, double gain) { return ReplayGainSettings_AddToHistory(seconds, gain); }
+private:
+ virtual int WASABICALL ReplayGainSettings_GetGain(ifc_metadata *metadata, double *gain, int *warning)=0;
+ virtual int WASABICALL ReplayGainSettings_AddToHistory(double seconds, double gain)=0;
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+};
diff --git a/Src/replicant/replicant/metadata/Android.mk b/Src/replicant/replicant/metadata/Android.mk
new file mode 100644
index 000000000..6a3a75072
--- /dev/null
+++ b/Src/replicant/replicant/metadata/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := replicant-metadata
+LOCAL_C_INCLUDES := $(ROOT_REPLICANT)
+LOCAL_CFLAGS := -fvisibility=hidden -fno-rtti
+
+LOCAL_SRC_FILES := metadata.cpp MetadataManager.cpp ArtworkManager.cpp
+
+LOCAL_STATIC_LIBRARIES := nu foundation
+LOCAL_SHARED_LIBRARIES := nx
+
+include $(BUILD_STATIC_LIBRARY)
+
+
diff --git a/Src/replicant/replicant/metadata/ArtworkManager.cpp b/Src/replicant/replicant/metadata/ArtworkManager.cpp
new file mode 100644
index 000000000..19a6b5220
--- /dev/null
+++ b/Src/replicant/replicant/metadata/ArtworkManager.cpp
@@ -0,0 +1,750 @@
+#include "ArtworkManager.h"
+#include "api__wasabi2_metadata.h"
+#include "nswasabi/ReferenceCounted.h"
+#include "metadata/MetadataKeys.h"
+#include "metadata/svc_metadata.h"
+#ifdef __linux__
+#include <alloca.h>
+#include <unistd.h>
+#include <dirent.h>
+#elif defined (__APPLE__)
+#include <CoreServices/CoreServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+#elif defined(_WIN32)
+#include <malloc.h>
+#include <strsafe.h>
+#endif
+
+static inline bool TestFlag(int flags, int flag_to_check)
+{
+ if (flags & flag_to_check)
+ return true;
+ return false;
+}
+
+ArtworkManager::ArtworkManager()
+{
+ for (size_t i=0;i<sizeof(album_file_specs)/sizeof(*album_file_specs);i++)
+ {
+ album_file_specs[i]=0;
+ }
+
+ for (size_t i=0;i<sizeof(supported_image_types)/sizeof(*supported_image_types);i++)
+ {
+ supported_image_types[i].extension=0;
+ supported_image_types[i].mime=0;
+ }
+}
+
+int ArtworkManager::FillImageType(supported_image_types_t &image_type, const char *extension, const char *mime)
+{
+ int ret = NXStringCreateWithUTF8(&image_type.extension, extension);
+ if (ret != NErr_Success)
+ return ret;
+
+ return NXStringCreateWithUTF8(&image_type.mime, mime);
+}
+
+int ArtworkManager::Initialize()
+{
+ int ret;
+
+ ret = FillImageType(supported_image_types[0], "jpg", "image/jpeg");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = FillImageType(supported_image_types[1], "jpeg", "image/jpeg");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = FillImageType(supported_image_types[2], "png", "image/png");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = FillImageType(supported_image_types[3], "gif", "image/gif");
+ if (ret != NErr_Success)
+ return ret;
+
+
+ /* make the list of files to check for MetadataKeys::ALBUM */
+ ret = NXStringCreateWithUTF8(&album_file_specs[0], "cover.");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateWithUTF8(&album_file_specs[1], "front.");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateWithUTF8(&album_file_specs[2], "album.");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateWithUTF8(&album_file_specs[3], "albumart.");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateWithUTF8(&album_file_specs[4], "Cover Art (front).");
+ if (ret != NErr_Success)
+ return ret;
+
+ ret = NXStringCreateWithUTF8(&album_file_specs[5], "folder.");
+ if (ret != NErr_Success)
+ return ret;
+
+ return NErr_Success;
+}
+
+void ArtworkManager::Shutdown()
+{
+ for (size_t i=0;i<sizeof(album_file_specs)/sizeof(*album_file_specs);i++)
+ {
+ NXStringRelease(album_file_specs[i]);
+ album_file_specs[i]=0;
+ }
+
+ for (size_t i=0;i<sizeof(supported_image_types)/sizeof(*supported_image_types);i++)
+ {
+ NXStringRelease(supported_image_types[i].extension);
+ supported_image_types[i].extension=0;
+ NXStringRelease(supported_image_types[i].mime);
+ supported_image_types[i].mime=0;
+ }
+}
+
+nx_string_t ArtworkManager::GetFileSpecForField(unsigned int field, size_t index)
+{
+ switch(field)
+ {
+ case MetadataKeys::ALBUM:
+ if (index < sizeof(album_file_specs)/sizeof(*album_file_specs))
+ return album_file_specs[index];
+ break;
+ }
+ return 0;
+}
+
+nx_string_t ArtworkManager::EnumerateExtension(size_t index)
+{
+ if (index < sizeof(supported_image_types)/sizeof(*supported_image_types))
+ return supported_image_types[index].extension;
+ return 0;
+}
+
+nx_string_t ArtworkManager::EnumerateMIME(size_t index)
+{
+ if (index < sizeof(supported_image_types)/sizeof(*supported_image_types))
+ return supported_image_types[index].mime;
+ return 0;
+}
+
+
+#ifdef _WIN32
+
+
+static uint64_t FileTimeToUnixTime(FILETIME *ft)
+{
+ ULARGE_INTEGER end;
+ memcpy(&end,ft,sizeof(end));
+ end.QuadPart -= 116444736000000000;
+ end.QuadPart /= 10000000; // 100ns -> seconds
+ return end.QuadPart;
+}
+
+int ArtworkManager::FindValidFile(nx_uri_t filepath, nx_string_t file_spec, artwork_t *artwork, data_flags_t flags)
+{
+ size_t search_spec_length;
+
+ search_spec_length = (filepath?filepath->len:0) + file_spec->len + 1 /* for * */ + 1 /* for null terminator */;
+ wchar_t search_spec[MAX_PATH] = {0};
+
+ if (filepath && filepath->len)
+ {
+ wchar_t path_end = filepath->string[filepath->len-1];
+ StringCchPrintf(search_spec, search_spec_length, L"%s%s%s*", filepath->string, path_end?L"":L"\\", file_spec->string);
+
+ }
+ else
+ {
+ StringCchPrintf(search_spec, search_spec_length, L"%s*", file_spec->string);
+ }
+
+ WIN32_FIND_DATA find_data;
+ HANDLE hFind = FindFirstFile(search_spec, &find_data);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ ReferenceCountedNXURI filename;
+ NXURICreateFromPath(&filename, find_data.cFileName, filepath);
+ nx_string_t extension;
+ size_t i;
+ for (i=0, extension = EnumerateExtension(i);extension;extension = EnumerateExtension(++i))
+ {
+ if (NXPathMatchExtension(filename, extension) == NErr_True)
+ {
+ FindClose(hFind);
+ if (artwork)
+ {
+ nx_data_t data=0;
+
+ if (flags != DATA_FLAG_NONE)
+ {
+ int ret;
+ if (TestFlag(flags, DATA_FLAG_DATA))
+ {
+ ret = NXDataCreateFromURI(&data, filename);
+ if (ret != NErr_Success)
+ return ret;
+ }
+ else
+ {
+ ret = NXDataCreateEmpty(&data);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (TestFlag(flags, DATA_FLAG_SOURCE_INFORMATION))
+ {
+ ret = NXDataSetSourceURI(data, filename);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+
+ nx_file_stat_s stat_buffer;
+ ret = NXFile_stat(filename, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+
+ ret = NXDataSetSourceStat(data, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+ }
+
+ if (TestFlag(flags, DATA_FLAG_MIME))
+ {
+ nx_string_t mime_type = EnumerateMIME(i);
+ ret = NXDataSetMIME(data, mime_type);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+
+
+ }
+ artwork->data = data;
+ artwork->width = 0;
+ artwork->height = 0;
+ }
+
+ return NErr_Success;
+ }
+ }
+ } while (FindNextFileW(hFind, &find_data));
+ FindClose(hFind);
+ }
+
+ return NErr_False;
+}
+#elif defined(__linux__)
+static int match_extension(const char *filename, const char *extension)
+{
+ size_t a, b;
+
+ if (!filename || !extension)
+ return 0;
+
+ a = strlen(filename);
+ b = strlen(extension);
+
+ if (!a)
+ return 0;
+
+ if (b > a)
+ return 0;
+
+ if (filename[a-b-1] != '.')
+ return 0;
+
+ do
+ {
+ if ((filename[--a] & ~0x20) != (extension[--b] & ~0x20))
+ return 0;
+ } while (b);
+
+ return 1;
+}
+
+int ArtworkManager::FindValidFile(nx_uri_t filepath, nx_string_t file_spec[], size_t spec_count, artwork_t *artwork, data_flags_t flags)
+{
+ int ret;
+ DIR *dir = opendir(filepath->string);
+ if (!dir)
+ return NErr_Empty;
+
+ dirent entryp;
+
+ struct dirent *entry;
+ while(readdir_r(dir, &entryp, &entry) == 0 && entry)
+ {
+ for (size_t spec=0;spec<spec_count;spec++)
+ {
+ if (!strncasecmp(entry->d_name, file_spec[spec]->string, file_spec[spec]->len))
+ {
+ nx_string_t ext;
+ size_t i;
+ for (i=0, ext = EnumerateExtension(i);ext;ext = EnumerateExtension(++i))
+ {
+ if (match_extension(entry->d_name, ext->string))
+ {
+ closedir(dir);
+ if (artwork)
+ {
+ ReferenceCountedNXURI uri_filename;
+ ReferenceCountedNXURI filename;
+ NXURICreateWithUTF8(&uri_filename, entry->d_name);
+ NXURICreateWithPath(&filename, uri_filename, filepath);
+
+ nx_data_t data=0;
+
+ if (flags != DATA_FLAG_NONE)
+ {
+ if (TestFlag(flags, DATA_FLAG_DATA))
+ {
+ ret = NXDataCreateFromURI(&data, filename);
+ if (ret != NErr_Success)
+ return ret;
+ }
+ else
+ {
+ ret = NXDataCreateEmpty(&data);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (TestFlag(flags, DATA_FLAG_SOURCE_INFORMATION))
+ {
+ ret = NXDataSetSourceURI(data, filename);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+
+ nx_file_stat_s stat_buffer;
+ ret = NXFile_stat(filename, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+
+ ret = NXDataSetSourceStat(data, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+ }
+
+ if (TestFlag(flags, DATA_FLAG_MIME))
+ {
+ nx_string_t mime_type = EnumerateMIME(i);
+ ret = NXDataSetMIME(data, mime_type);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+
+
+ }
+ artwork->data = data;
+ artwork->width = 0;
+ artwork->height = 0;
+ }
+ return NErr_Success;
+ }
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+
+ return NErr_False;
+}
+#elif defined(__APPLE__)
+
+
+int ArtworkManager::IsMatchingFile(nx_uri_t file, nx_string_t file_spec, size_t *extension_idx)
+{
+ CFStringRef lastComponent = CFURLCopyLastPathComponent(file);
+ if (NULL == lastComponent)
+ return NErr_False;
+
+ int err = NErr_False;
+ CFIndex file_spec_len = CFStringGetLength(file_spec);
+
+ if (kCFCompareEqualTo == CFStringCompareWithOptionsAndLocale(lastComponent, file_spec,
+ CFRangeMake(0, file_spec_len),
+ kCFCompareCaseInsensitive, NULL))
+ {
+ nx_string_t extension;
+ size_t i;
+
+ for (i=0, extension = EnumerateExtension(i);NULL != extension;extension = EnumerateExtension(++i))
+ {
+ CFIndex extension_len = CFStringGetLength(extension);
+ if (kCFCompareEqualTo == CFStringCompareWithOptionsAndLocale(lastComponent, extension,
+ CFRangeMake(file_spec_len, extension_len),
+ kCFCompareCaseInsensitive, NULL))
+ {
+ if (NULL != extension_idx)
+ *extension_idx = i;
+
+ err = NErr_True;
+ }
+
+ if (NErr_False != err)
+ break;
+ }
+ }
+
+ CFRelease(lastComponent);
+ return err;
+}
+
+int ArtworkManager::FillFileData(nx_uri_t file, size_t extension_idx,
+ artwork_t *artwork, data_flags_t flags)
+{
+ if (NULL == artwork)
+ return NErr_BadParameter;
+
+ nx_data_t data = NULL;
+ ns_error_t ret;
+
+ if (flags != DATA_FLAG_NONE)
+ {
+ if (TestFlag(flags, DATA_FLAG_DATA))
+ {
+ ret = NXDataCreateFromURI(&data, file);
+ if (ret != NErr_Success)
+ return ret;
+ }
+ else
+ {
+ ret = NXDataCreateEmpty(&data);
+ if (ret != NErr_Success)
+ return ret;
+
+ if (TestFlag(flags, DATA_FLAG_SOURCE_INFORMATION))
+ {
+ ret = NXDataSetSourceURI(data, file);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+
+ nx_file_stat_s stat_buffer;
+ ret = NXFile_stat(file, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+
+ ret = NXDataSetSourceStat(data, &stat_buffer);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+ }
+
+ if (TestFlag(flags, DATA_FLAG_MIME))
+ {
+ nx_string_t mime_type = EnumerateMIME(extension_idx);
+ ret = NXDataSetMIME(data, mime_type);
+ if (ret != NErr_Success)
+ {
+ NXDataRelease(data);
+ return ret;
+ }
+ }
+
+
+ }
+ artwork->data = data;
+ artwork->width = 0;
+ artwork->height = 0;
+
+ return ret;
+}
+
+int ArtworkManager::FindValidFile(nx_uri_t directory, nx_string_t file_spec,
+ artwork_t *artwork, data_flags_t flags)
+{
+ if (NULL == directory)
+ return NErr_BadParameter;
+
+ FSRef directory_ref;
+ if (false == CFURLGetFSRef(directory, &directory_ref))
+ return NErr_FileNotFound;
+
+ int err;
+ OSErr os_err;
+ FSIterator iterator;
+
+ os_err = FSOpenIterator(&directory_ref, kFSIterateFlat, &iterator);
+ if (noErr != os_err)
+ return NErr_Error;
+
+
+ FSCatalogInfoBitmap catalog_info_bitmap = kFSCatInfoNodeFlags;
+
+ const size_t maximum_count = ((4096 * 4) / (sizeof(FSCatalogInfo) + sizeof(FSRef)));
+
+ FSCatalogInfo *catalog_info = (FSCatalogInfo *)malloc(maximum_count * sizeof(FSCatalogInfo));
+ FSRef *item_ref = (FSRef *)malloc(maximum_count * sizeof(FSRef));
+ if (NULL == item_ref
+ || NULL == catalog_info)
+ {
+ err = NErr_OutOfMemory;
+ }
+ else
+ {
+ err = NErr_False;
+ while(noErr == os_err)
+ {
+ ItemCount actual_count;
+
+ os_err = FSGetCatalogInfoBulk(iterator, maximum_count, &actual_count, NULL,
+ catalog_info_bitmap, catalog_info,
+ item_ref, NULL, NULL);
+
+ // Process all items received
+ if(noErr == os_err
+ || errFSNoMoreItems == os_err)
+ {
+ UInt32 index;
+ for(index = 0; index < actual_count; index += 1)
+ {
+ if(0 == (catalog_info[index].nodeFlags & kFSNodeIsDirectoryMask))
+ {
+ CFURLRef file_uri = CFURLCreateFromFSRef(NULL, &item_ref[index]);
+ if (NULL != file_uri)
+ {
+ size_t extension_idx;
+ if (NErr_True == IsMatchingFile(file_uri, file_spec, &extension_idx))
+ {
+ if (NULL != artwork
+ && DATA_FLAG_NONE != flags)
+ {
+ err = FillFileData(file_uri, extension_idx, artwork, flags);
+ }
+
+ }
+ CFRelease(file_uri);
+
+ if (NErr_False != err)
+ break;
+ }
+ }
+ }
+ }
+
+ if (NErr_False != err)
+ break;
+ }
+
+ }
+
+ FSCloseIterator(iterator);
+
+ if (NULL != catalog_info)
+ free(catalog_info);
+ if (NULL != item_ref)
+ free(item_ref);
+
+ return err;
+}
+
+#else
+// TODO: other ports
+#endif
+
+int ArtworkManager::Artwork_GetArtwork(nx_uri_t filename, unsigned int field, artwork_t *artwork, data_flags_t flags, nx_time_unix_64_t *out_file_modified)
+{
+ ifc_metadata *metadata = 0;
+ int ret = REPLICANT_API_METADATA->CreateMetadata(&metadata, filename);
+
+ // Step 1) try embedded
+ if (ret == NErr_Success)
+ {
+ if (out_file_modified)
+ {
+ int64_t modified;
+ if (metadata->GetInteger(MetadataKeys::FILE_TIME, 0, &modified) == NErr_Success)
+ *out_file_modified = (nx_time_unix_64_t)modified;
+ else
+ {
+ nx_file_stat_s stat_buffer;
+ if (NXFile_stat(filename, &stat_buffer) == NErr_Success)
+ *out_file_modified=stat_buffer.modified_time;
+ else
+ *out_file_modified=0;
+ }
+ }
+
+ if (metadata->GetArtwork(field, 0, artwork, flags) == NErr_Success)
+ {
+ metadata->Release();
+ return NErr_Success;
+ }
+ }
+ else
+ {
+ if (out_file_modified)
+ {
+ nx_file_stat_s stat_buffer;
+ if (NXFile_stat(filename, &stat_buffer) == NErr_Success)
+ *out_file_modified=stat_buffer.modified_time;
+ else
+ *out_file_modified=0;
+ }
+ }
+
+ ReferenceCountedNXURI filepath;
+ NXURICreateRemovingFilename(&filepath, filename);
+
+ // Step 2) try svc_art_provider
+
+
+ // Step 3) album name.jpg
+ if (metadata)
+ {
+ ReferenceCountedNXString field_name;
+ if (metadata->GetField(field, 0, &field_name) == NErr_Success)
+ {
+ // nx_uri_t filepart, search_spec;
+ size_t byte_count=0;
+ int ret = NXStringGetBytesSize(&byte_count, field_name, nx_charset_utf8, 0);
+ if (ret == NErr_DirectPointer || ret == NErr_Success)
+ {
+ char *ptr = (char *)malloc(byte_count+3); // +1 for prefixed ".", +1 for "." and +1 for terminator
+ if (ptr)
+ {
+ ptr[0]='.';
+ size_t length;
+ NXStringGetBytes(&length, field_name, ptr+1, byte_count, nx_charset_utf8, 0);
+ length++; // adjust length because of the prefix
+ for (size_t i=1;i<length;i++)
+ {
+ switch(ptr[i])
+ {
+ case '?':
+ case '*':
+ case '|':
+ ptr[i] = '_';
+ break;
+ case '/':
+ case '\\':
+ case ':':
+ ptr[i] = '-';
+ break;
+ case '\"':
+ ptr[i] = '\'';
+ break;
+ case '<':
+ ptr[i] = '(';
+ break;
+ case '>':
+ ptr[i] = ')';
+ break;
+ }
+ }
+ ptr[length++]='.';
+ ptr[length]=0;
+
+ // try with prefixed '.' first
+ ReferenceCountedNXString file_spec;
+ ret = NXStringCreateWithUTF8(&file_spec, ptr);
+ if (ret == NErr_Success)
+ {
+#if defined(__linux__)
+ nx_string_t temp = file_spec;
+ ret = FindValidFile(filepath, &temp, 1, artwork, flags);
+#else
+ ret = FindValidFile(filepath, file_spec, artwork, flags);
+#endif
+ if (ret == NErr_Success)
+ {
+ metadata->Release();
+ free(ptr);
+ return NErr_Success;
+ }
+ }
+
+ // now try with no prefixed '.'
+ ret = NXStringCreateWithUTF8(&file_spec, ptr+1);
+ free(ptr);
+ if (ret == NErr_Success)
+ {
+#if defined(__linux__)
+ nx_string_t temp = file_spec;
+ ret = FindValidFile(filepath, &temp, 1, artwork, flags);
+#else
+ ret = FindValidFile(filepath, file_spec, artwork, flags);
+#endif
+
+ if (ret == NErr_Success)
+ {
+ metadata->Release();
+ return NErr_Success;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (metadata)
+ metadata->Release();
+
+ // Step 4) .nfo
+
+ // Step 5) <field>.jpg
+#if defined(__linux__)
+ if (field == MetadataKeys::ALBUM)
+ {
+ int ret = FindValidFile(filepath, album_file_specs, sizeof(album_file_specs)/sizeof(*album_file_specs), artwork, flags);
+ if (ret == NErr_Success)
+ return NErr_Success;
+ }
+#else
+ size_t index=0;
+ nx_string_t field_name;
+ for (field_name = GetFileSpecForField(field, index);field_name;field_name = GetFileSpecForField(field, ++index))
+ {
+
+ int ret = FindValidFile(filepath, field_name, artwork, flags);
+
+ if (ret == NErr_Success)
+ return NErr_Success;
+ }
+#endif
+ return NErr_Empty;
+}
diff --git a/Src/replicant/replicant/metadata/ArtworkManager.h b/Src/replicant/replicant/metadata/ArtworkManager.h
new file mode 100644
index 000000000..e2e2035c6
--- /dev/null
+++ b/Src/replicant/replicant/metadata/ArtworkManager.h
@@ -0,0 +1,52 @@
+#pragma once
+#include "metadata/api_artwork.h"
+#include "nswasabi/ServiceName.h"
+#include "nx/nx.h"
+
+
+#if 0 // TODO
+class ArtworkEnumerator
+{
+public:
+};
+#endif
+
+class ArtworkManager : public api_artwork
+{
+public:
+ WASABI_SERVICE_NAME("Artwork Manager API");
+ ArtworkManager();
+
+ int Initialize();
+ void Shutdown();
+private:
+ /* returns the data for the first piece of artwork found
+ pass NULL for any of the values that you don't care about */
+ int WASABICALL Artwork_GetArtwork(nx_uri_t filename, unsigned int field, artwork_t *artwork, data_flags_t flags, nx_time_unix_64_t *filename_modified);
+
+#if defined(__linux__)
+ int FindValidFile(nx_uri_t filepath, nx_string_t file_spec[], size_t spec_count, artwork_t *artwork, data_flags_t flags);
+#else
+ int FindValidFile(nx_uri_t filepath, nx_string_t file_spec, artwork_t *artwork, data_flags_t flags);
+#endif
+
+ nx_string_t GetFileSpecForField(unsigned int field, size_t index);
+ nx_string_t EnumerateExtension(size_t index);
+ nx_string_t EnumerateMIME(size_t index);
+
+ // TODO: this will go away at some point, when we have image loaders ready
+ struct supported_image_types_t
+ {
+ nx_string_t extension;
+ nx_string_t mime;
+ } supported_image_types[4];
+ static int FillImageType(supported_image_types_t &image_type, const char *extension, const char *mime);
+ nx_string_t album_file_specs[6];
+
+
+#ifdef __APPLE__
+private:
+ int IsMatchingFile(nx_uri_t file, nx_string_t file_spec, size_t *extension_idx);
+ int FillFileData(nx_uri_t file, size_t extension_idx, artwork_t *artwork, data_flags_t flags);
+#endif
+};
diff --git a/Src/replicant/replicant/metadata/Makefile b/Src/replicant/replicant/metadata/Makefile
new file mode 100644
index 000000000..793b5752f
--- /dev/null
+++ b/Src/replicant/replicant/metadata/Makefile
@@ -0,0 +1,53 @@
+MODULE_NAME := replicant-metadata
+
+OS := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+
+ifeq ($(OS),Darwin)
+LDFLAGS = -framework CoreFoundation -framework Carbon
+endif
+
+CPPSOURCES := metadata.cpp MetadataManager.cpp ArtworkManager.cpp
+#CSOURCES := utf.c ByteReader.c ByteWriter.c
+
+LIBRARY_FILENAME := lib$(MODULE_NAME).a
+OUTPUT_PATH := ../../build/$(MODULE_NAME)
+ARCHIVE_PATH := ../../build/lib
+LIBRARY_FILEPATH := ../../build/lib/$(LIBRARY_FILENAME)
+
+CPPOBJS := $(patsubst %.cpp,$(OUTPUT_PATH)/%.o,$(CPPSOURCES))
+CPPDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(CPPOBJS))
+COBJS := $(patsubst %.c,$(OUTPUT_PATH)/%.o,$(CSOURCES))
+CDEPS := $(patsubst %.o,$(OUTPUT_PATH)/%.d,$(COBJS))
+
+OBJS := $(CPPOBJS) $(COBJS)
+DEPS := $(CPPDEPS) $(CDEPS)
+
+CFLAGS=-I../..
+CPPFLAGS := ${CFLAGS}
+
+
+
+build: build-dir $(LIBRARY_FILEPATH)
+
+build-dir:
+ @mkdir -p $(OUTPUT_PATH) > /dev/null 2> /dev/null
+ @mkdir -p $(ARCHIVE_PATH) > /dev/null 2> /dev/null
+
+dep:
+ @rm ${DEPS}
+
+$(OUTPUT_PATH)/%.o: %.cpp
+ #@echo Compiling $*.cpp
+ @$(CXX) $(CPPFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.cpp -o $(OUTPUT_PATH)/$*.o
+
+$(OUTPUT_PATH)/%.o: %.c
+ #@echo Compiling $*.c
+ @$(CC) $(CFLAGS) -MMD -MF $(OUTPUT_PATH)/$*.d -MT $(OUTPUT_PATH)/$*.o -c $*.c -o $(OUTPUT_PATH)/$*.o
+
+$(LIBRARY_FILEPATH): ${OBJS}
+ @$(AR) rcs $@ ${OBJS}
+
+clean:
+ -rm -f ${OBJS} $(LIBRARY_FILENAME) ${DEPS}
+
+-include $(DEPS)
diff --git a/Src/replicant/replicant/metadata/MetadataManager.cpp b/Src/replicant/replicant/metadata/MetadataManager.cpp
new file mode 100644
index 000000000..398831cee
--- /dev/null
+++ b/Src/replicant/replicant/metadata/MetadataManager.cpp
@@ -0,0 +1,358 @@
+#include "MetadataManager.h"
+#include "metadata/genres.h"
+#include "metadata/svc_metadata.h"
+#include "nx/nxpath.h"
+
+#include "api__wasabi2_metadata.h"
+
+MetadataManager::MetadataManager()
+{
+ NXOnceInit(&fields_once);
+ NXOnceInit(&genres_once);
+
+ memset(&lookup, 0, sizeof(lookup));
+ memset(&genres, 0, sizeof(genres));
+}
+
+MetadataManager::~MetadataManager()
+{
+ Shutdown(); // this should have already been called
+}
+
+void MetadataManager::Shutdown()
+{
+ // Release all the lookup entries
+ for (size_t i = 0; i < MetadataKeys::NUM_OF_METADATA_KEYS; i++)
+ {
+ NXStringRelease(lookup[i]);
+ }
+
+ // Release all the extended lookup entries
+ for ( nx_string_t l_lookup_extended : lookup_extended )
+ NXStringRelease( l_lookup_extended );
+
+ // Release all the extended lookup entries
+ for (size_t i = 0; i < metadata_genre_list_size; i++)
+ {
+ NXStringRelease(genres[i]);
+ }
+
+ lookup_extended.clear();
+}
+
+using namespace MetadataKeys;
+#define METADATA_LOOKUP(x) { nx_string_t temp=0; NXStringCreateWithUTF8(&temp, #x); me->lookup[x]=temp; }
+int MetadataManager::InitializeMetadataFieldLookup(nx_once_t once, void *_me, void **unused)
+{
+ MetadataManager *me = (MetadataManager *)_me;
+
+ METADATA_LOOKUP(ARTIST);
+ METADATA_LOOKUP(ALBUM_ARTIST);
+ METADATA_LOOKUP(ALBUM);
+ METADATA_LOOKUP(TITLE);
+ METADATA_LOOKUP(URI);
+ METADATA_LOOKUP(GENRE);
+ METADATA_LOOKUP(YEAR);
+ METADATA_LOOKUP(TRACK);
+ METADATA_LOOKUP(DISC);
+ METADATA_LOOKUP(BITRATE);
+ METADATA_LOOKUP(COMPOSER);
+ METADATA_LOOKUP(PUBLISHER);
+ METADATA_LOOKUP(BPM);
+ METADATA_LOOKUP(COMMENT);
+ METADATA_LOOKUP(DISCS);
+ METADATA_LOOKUP(FILE_SIZE);
+ METADATA_LOOKUP(FILE_TIME);
+ METADATA_LOOKUP(LENGTH);
+ METADATA_LOOKUP(PLAY_COUNT);
+ METADATA_LOOKUP(RATING);
+ METADATA_LOOKUP(SERVER);
+ METADATA_LOOKUP(MIME_TYPE);
+ METADATA_LOOKUP(TRACK_GAIN);
+ METADATA_LOOKUP(TRACK_PEAK);
+ METADATA_LOOKUP(ALBUM_GAIN);
+ METADATA_LOOKUP(ALBUM_PEAK);
+ METADATA_LOOKUP(TRACKS);
+ METADATA_LOOKUP(PREGAP);
+ METADATA_LOOKUP(POSTGAP);
+ METADATA_LOOKUP(STAT);
+ METADATA_LOOKUP(CATEGORY);
+ METADATA_LOOKUP(DIRECTOR);
+ METADATA_LOOKUP(PRODUCER);
+ METADATA_LOOKUP(LAST_PLAY);
+ METADATA_LOOKUP(LAST_UPDATE);
+ return 1;
+}
+#undef METADATA_LOOKUP
+
+
+int MetadataManager::InitializeGenres(nx_once_t once, void *_me, void **)
+{
+ MetadataManager *me = (MetadataManager *)_me;
+ size_t num_genres = sizeof(metadata_genre_list) / sizeof(*metadata_genre_list);
+
+ for (size_t i=0;i<num_genres;i++)
+ {
+ nx_string_t genre_name=0;
+ if (NXStringCreateWithUTF8(&genre_name, metadata_genre_list[i]) == NErr_Success)
+ me->genres[i]=genre_name;
+ }
+ return 1;
+}
+
+
+ns_error_t MetadataManager::Metadata_RegisterField(nx_string_t field_name, int *field)
+{
+ // see if we have already registered this field
+ int ret = Metadata_GetFieldKey(field_name, field);
+ if (ret == NErr_Success)
+ return ret;
+
+ //If not then register the field assign an id and pass it back
+ nx_string_t added_field = NXStringRetain(field_name);
+ lookup_extended.push_back(added_field);
+ //ret = lookup_extended.push_back(added_field);
+ //if (ret != NErr_Success)
+ //{
+ // NXStringRelease(added_field);
+ // return ret;
+ //}
+
+ // Return the ID which is derived from the OFFSET (1000) zero based, so first id assigned will be 1000
+ *field = (int)lookup_extended.size() + MetadataKeys::EXTENDED_KEYS_OFFSET - 1;
+ return NErr_Success;
+}
+
+ns_error_t MetadataManager::Metadata_GetFieldKey(nx_string_t field_name, int *field)
+{
+ // Initialize our lookup table if this is the first time we are being called.
+ NXOnce(&fields_once, InitializeMetadataFieldLookup, this);
+
+ for (size_t i = 0; i < MetadataKeys::NUM_OF_METADATA_KEYS; i++)
+ {
+ if (lookup[i] && NXStringKeywordCompare(field_name, lookup[i]) == NErr_True)
+ {
+ *field = (int)i;
+ return NErr_Success;
+ }
+ }
+
+ for (size_t i = 0; i < lookup_extended.size(); i++)
+ {
+ if (NXStringKeywordCompare(field_name, lookup_extended[i]) == NErr_True)
+ {
+ *field = (int)i + MetadataKeys::EXTENDED_KEYS_OFFSET;
+ return NErr_Success;
+ }
+ }
+
+ return NErr_Unknown;
+}
+
+ns_error_t MetadataManager::Metadata_GetFieldName(int field_key, nx_string_t *name)
+{
+ // Initialize our lookup table if this is the first time we are being called.
+ NXOnce(&fields_once, InitializeMetadataFieldLookup, this);
+
+ if (field_key >= MetadataKeys::EXTENDED_KEYS_OFFSET)
+ {
+ field_key -= MetadataKeys::EXTENDED_KEYS_OFFSET;
+ if (field_key < (int)lookup_extended.size())
+ {
+ *name = NXStringRetain(lookup_extended.at(field_key));
+ return NErr_Success;
+ }
+ else
+ return NErr_Unknown;
+ }
+
+ if (field_key < MetadataKeys::NUM_OF_METADATA_KEYS)
+ {
+ *name = NXStringRetain(lookup[field_key]);
+ return NErr_Success;
+ }
+
+ return NErr_Unknown;
+}
+
+ns_error_t MetadataManager::Metadata_GetGenre(uint8_t genre_id, nx_string_t *genre)
+{
+ NXOnce(&genres_once, InitializeGenres, this);
+ if (genre_id >= metadata_genre_list_size)
+ return NErr_Unknown;
+
+ nx_string_t name = genres[genre_id];
+ if (!name) /* shouldn't happen unless we were out of memory during load */
+ return NErr_Unknown;
+
+ *genre = NXStringRetain(name);
+ return NErr_Success;
+}
+
+ns_error_t MetadataManager::Metadata_GetGenreID(nx_string_t genre, uint8_t *genre_id)
+{
+ NXOnce(&genres_once, InitializeGenres, this);
+ for (size_t i = 0; i < metadata_genre_list_size; i++)
+ {
+ nx_string_t name = genres[i];
+ if (name && NXStringKeywordCompare(genre, name) == NErr_True)
+ {
+ *genre_id = (uint8_t)i;
+ return NErr_Success;
+ }
+ }
+
+ return NErr_Unknown;
+}
+
+//#include "nswasabi/AutoCharNX.h"
+
+ns_error_t MetadataManager::Metadata_SupportedFilename(nx_uri_t filename)
+{
+
+ GUID metadata_guid = svc_metadata::GetServiceType();
+ size_t n = WASABI2_API_SVC->GetServiceCount(metadata_guid);
+ //printf("Looking through %d services for extensions\n", n );
+ for (size_t i=0; i<n; i++)
+ {
+ ifc_serviceFactory *sf = WASABI2_API_SVC->EnumService(metadata_guid,i);
+ if (sf)
+ {
+ nx_string_t service_name = sf->GetServiceName();
+
+ svc_metadata * l = (svc_metadata*)sf->GetInterface();
+ if (l)
+ {
+ //printf("Looking through '%s' for extensions\n", AutoCharPrintfUTF8(service_name) );
+ for (unsigned int index=0;;index++)
+ {
+ nx_string_t nx_extension;
+ if (l->EnumerateExtensions(index, &nx_extension) == NErr_Success)
+ {
+ //printf("Looking at extension, '%s'\n", AutoCharPrintfUTF8(nx_extension) );
+ if (NXPathMatchExtension(filename, nx_extension) == NErr_True)
+ {
+ l->Release();
+ NXStringRelease(nx_extension);
+ return NErr_True;
+ }
+ NXStringRelease(nx_extension);
+ }
+ else
+ {
+ break;
+ }
+ }
+ l->Release();
+ }
+ }
+ }
+ return NErr_False;
+}
+
+static ifc_metadata *FindMetadataTryAgain(size_t i, size_t n, nx_uri_t filename, svc_metadata *fallback)
+{
+ ifc_metadata *metadata;
+
+ GUID metadata_guid = svc_metadata::GetServiceType();
+ for (;i<n; i++)
+ {
+ ifc_serviceFactory *sf = WASABI2_API_SVC->EnumService(metadata_guid, i);
+ if (sf)
+ {
+ svc_metadata * l = (svc_metadata *)sf->GetInterface();
+ if (l)
+ {
+ //printf("searching through, '%x'\n", l);
+ ns_error_t ret = l->CreateMetadata(0, filename, &metadata);
+ //printf("metadata creation returned, '%d'\n", ret);
+ if (ret == NErr_Success)
+ {
+ l->Release();
+ return metadata;
+ }
+ else if (ret == NErr_TryAgain)
+ {
+ metadata = FindMetadataTryAgain(i+1, n, filename, l);
+ //printf("trying again got metadata, '%x'\n", metadata);
+ l->Release();
+ return metadata;
+ }
+ l->Release();
+ }
+ }
+ }
+
+ if (fallback && fallback->CreateMetadata(1, filename, &metadata) == NErr_Success)
+ {
+ return metadata;
+ }
+
+ return 0;
+}
+
+ns_error_t MetadataManager::Metadata_CreateMetadata(ifc_metadata **out_metadata, nx_uri_t filename)
+{
+ GUID metadata_guid = svc_metadata::GetServiceType();
+ size_t n = WASABI2_API_SVC->GetServiceCount(metadata_guid);
+ ifc_metadata *metadata = FindMetadataTryAgain(0, n, filename, 0);
+ if (metadata)
+ {
+ *out_metadata = metadata;
+ return NErr_Success;
+ }
+ else
+ return NErr_NoMatchingImplementation;
+}
+
+static ifc_metadata_editor *FindMetadataEditorTryAgain(size_t i, size_t n, nx_uri_t filename, svc_metadata *fallback)
+{
+ ifc_metadata_editor *metadata;
+
+ GUID metadata_guid = svc_metadata::GetServiceType();
+ for (;i<n; i++)
+ {
+ ifc_serviceFactory *sf = WASABI2_API_SVC->EnumService(metadata_guid, i);
+ if (sf)
+ {
+ svc_metadata * l = (svc_metadata *)sf->GetInterface();
+ if (l)
+ {
+ ns_error_t ret = l->CreateMetadataEditor(0, filename, &metadata);
+ if (ret == NErr_Success)
+ {
+ l->Release();
+ return metadata;
+ }
+ else if (ret == NErr_TryAgain)
+ {
+ metadata = FindMetadataEditorTryAgain(i+1, n, filename, l);
+ l->Release();
+ return metadata;
+ }
+ l->Release();
+ }
+ }
+ }
+
+ if (fallback && fallback->CreateMetadataEditor(1, filename, &metadata) == NErr_Success)
+ {
+ return metadata;
+ }
+
+ return 0;
+}
+
+ns_error_t MetadataManager::Metadata_CreateMetadataEditor(ifc_metadata_editor **out_metadata, nx_uri_t filename)
+{
+ GUID metadata_guid = svc_metadata::GetServiceType();
+ size_t n = WASABI2_API_SVC->GetServiceCount(metadata_guid);
+ ifc_metadata_editor *metadata = FindMetadataEditorTryAgain(0, n, filename, 0);
+ if (metadata)
+ {
+ *out_metadata = metadata;
+ return NErr_Success;
+ }
+ else
+ return NErr_NoMatchingImplementation;
+}
diff --git a/Src/replicant/replicant/metadata/MetadataManager.h b/Src/replicant/replicant/metadata/MetadataManager.h
new file mode 100644
index 000000000..0a66b9ec7
--- /dev/null
+++ b/Src/replicant/replicant/metadata/MetadataManager.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "metadata/api_metadata.h"
+#include "metadata/MetadataKeys.h"
+
+#include "nswasabi/ServiceName.h"
+
+#include "nx/nxstring.h"
+#include "nx/nxonce.h"
+#include <vector>
+
+#include "metadata/genres.h"
+
+
+
+class MetadataManager : public api_metadata
+{
+public:
+ WASABI_SERVICE_NAME("Metadata Manager");
+ MetadataManager();
+ ~MetadataManager();
+
+ void Shutdown();
+
+ /* api_metadata implementation */
+ ns_error_t WASABICALL Metadata_RegisterField(nx_string_t field_name, int *field);
+ ns_error_t WASABICALL Metadata_GetFieldKey(nx_string_t field_name, int *field);
+ ns_error_t WASABICALL Metadata_GetFieldName(int field_key, nx_string_t *name);
+ ns_error_t WASABICALL Metadata_GetGenre(uint8_t genre_id, nx_string_t *genre);
+ ns_error_t WASABICALL Metadata_GetGenreID(nx_string_t genre, uint8_t *genre_id);
+ ns_error_t WASABICALL Metadata_SupportedFilename(nx_uri_t filename);
+ ns_error_t WASABICALL Metadata_CreateMetadata(ifc_metadata **out_metadata, nx_uri_t filename);
+ ns_error_t WASABICALL Metadata_CreateMetadataEditor(ifc_metadata_editor **out_metadata, nx_uri_t filename);
+private:
+ nx_once_value_t fields_once, genres_once;
+ nx_string_t lookup[MetadataKeys::NUM_OF_METADATA_KEYS]; // Lookup list for all the standard "hard coded" fields
+
+ std::vector<nx_string_t> lookup_extended; // Lookup list for all the registered extended lookup entries
+ nx_string_t genres[metadata_genre_list_size];
+
+
+ static int NX_ONCE_API InitializeGenres(nx_once_t once, void *me, void **);
+ static int NX_ONCE_API InitializeMetadataFieldLookup(nx_once_t once, void *me, void **);
+};
+
diff --git a/Src/replicant/replicant/metadata/VERSION b/Src/replicant/replicant/metadata/VERSION
new file mode 100644
index 000000000..ea710abb9
--- /dev/null
+++ b/Src/replicant/replicant/metadata/VERSION
@@ -0,0 +1 @@
+1.2 \ No newline at end of file
diff --git a/Src/replicant/replicant/metadata/api__wasabi2_metadata.h b/Src/replicant/replicant/metadata/api__wasabi2_metadata.h
new file mode 100644
index 000000000..62363475e
--- /dev/null
+++ b/Src/replicant/replicant/metadata/api__wasabi2_metadata.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "service/api_service.h"
+extern api_service *metadata_service_manager;
+#define WASABI2_API_SVC metadata_service_manager
+
+#include "MetadataManager.h"
+extern MetadataManager metadata_manager;
+#define REPLICANT_API_METADATA (&metadata_manager)
diff --git a/Src/replicant/replicant/metadata/metadata.cpp b/Src/replicant/replicant/metadata/metadata.cpp
new file mode 100644
index 000000000..5534b0b3c
--- /dev/null
+++ b/Src/replicant/replicant/metadata/metadata.cpp
@@ -0,0 +1,39 @@
+#include "api__wasabi2_metadata.h"
+#include "metadata.h"
+#include "ArtworkManager.h"
+#include "nswasabi/singleton.h"
+#include "MetadataManager.h"
+
+api_service *WASABI2_API_SVC=0;
+MetadataManager metadata_manager;
+static ArtworkManager artwork_manager;
+
+static SingletonServiceFactory<MetadataManager, api_metadata> metadata_factory;
+static SingletonServiceFactory<ArtworkManager, api_artwork> artwork_factory;
+
+int Replicant_Metadata_Initialize(api_service *service_manager)
+{
+ WASABI2_API_SVC = service_manager;
+#ifndef REPLICANT_NO_ARTWORKMANAGER
+ int ret = artwork_manager.Initialize();
+ if (ret != NErr_Success)
+ return ret;
+#endif
+
+ metadata_factory.Register(WASABI2_API_SVC, &metadata_manager);
+#ifndef REPLICANT_NO_ARTWORKMANAGER
+ artwork_factory.Register(WASABI2_API_SVC, &artwork_manager);
+#endif
+ return NErr_Success;
+}
+
+void Replicant_Metadata_Shutdown()
+{
+#ifndef REPLICANT_NO_ARTWORKMANAGER
+ artwork_manager.Shutdown();
+#endif
+ metadata_factory.Deregister(WASABI2_API_SVC);
+#ifndef REPLICANT_NO_ARTWORKMANAGER
+ artwork_factory.Deregister(WASABI2_API_SVC);
+#endif
+}
diff --git a/Src/replicant/replicant/metadata/metadata.h b/Src/replicant/replicant/metadata/metadata.h
new file mode 100644
index 000000000..cc66730c5
--- /dev/null
+++ b/Src/replicant/replicant/metadata/metadata.h
@@ -0,0 +1,11 @@
+#pragma once
+#include "service/api_service.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ int Replicant_Metadata_Initialize(api_service *service_manager);
+ void Replicant_Metadata_Shutdown();
+#ifdef __cplusplus
+}
+#endif
diff --git a/Src/replicant/replicant/metadata/metadata.sln b/Src/replicant/replicant/metadata/metadata.sln
new file mode 100644
index 000000000..c4611f7db
--- /dev/null
+++ b/Src/replicant/replicant/metadata/metadata.sln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "metadata", "metadata.vcxproj", "{E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}.Debug|x64.ActiveCfg = Debug|x64
+ {E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}.Debug|x64.Build.0 = Debug|x64
+ {E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}.Release|Win32.ActiveCfg = Release|Win32
+ {E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}.Release|x64.ActiveCfg = Release|x64
+ {E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EEFE0D02-6A40-4235-A725-1E1B2F0050CC}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/replicant/replicant/metadata/metadata.vcxproj b/Src/replicant/replicant/metadata/metadata.vcxproj
new file mode 100644
index 000000000..df9572787
--- /dev/null
+++ b/Src/replicant/replicant/metadata/metadata.vcxproj
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{E2FD86D4-1A1C-49EB-BAB8-B63D594CC956}</ProjectGuid>
+ <RootNamespace>metadata</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN32;_DEBUG;_LIB;REPLICANT_NO_ARTWORKMANAGER;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN64;_DEBUG;_LIB;REPLICANT_NO_ARTWORKMANAGER;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;REPLICANT_NO_ARTWORKMANAGER;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4018;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN64;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;REPLICANT_NO_ARTWORKMANAGER;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4018;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Lib>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="ArtworkManager.cpp" />
+ <ClCompile Include="metadata.cpp" />
+ <ClCompile Include="MetadataManager.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__wasabi2_metadata.h" />
+ <ClInclude Include="ArtworkManager.h" />
+ <ClInclude Include="metadata.h" />
+ <ClInclude Include="MetadataManager.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/replicant/metadata/metadata.vcxproj.filters b/Src/replicant/replicant/metadata/metadata.vcxproj.filters
new file mode 100644
index 000000000..7a9a3f8e7
--- /dev/null
+++ b/Src/replicant/replicant/metadata/metadata.vcxproj.filters
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="ArtworkManager.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="metadata.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MetadataManager.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__wasabi2_metadata.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ArtworkManager.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="metadata.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MetadataManager.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{6b37411b-5610-4520-b9a1-12cb4e719928}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{2a9c541a-5355-4275-b173-566838fa5925}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{2a62aedb-8db2-491a-821b-f266582f7d9a}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/replicant/metadata/precomp.h b/Src/replicant/replicant/metadata/precomp.h
new file mode 100644
index 000000000..d597db5d3
--- /dev/null
+++ b/Src/replicant/replicant/metadata/precomp.h
@@ -0,0 +1,12 @@
+//
+// precomp.h
+// replicant-metadata
+//
+
+#include "foundation/error.h"
+#include "foundation/types.h"
+
+#ifdef __cplusplus
+#include "api.h"
+#include "metadata/metadata.h"
+#endif
diff --git a/Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/project.pbxproj b/Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..fd867e41c
--- /dev/null
+++ b/Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/project.pbxproj
@@ -0,0 +1,457 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 00CC775A1524DA3F004886ED /* metadata-prepare */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 00CC775B1524DA3F004886ED /* Build configuration list for PBXAggregateTarget "metadata-prepare" */;
+ buildPhases = (
+ 00CC77671524DB6A004886ED /* Generate Version Info */,
+ );
+ dependencies = (
+ 00E6E7641548E8BA005C9962 /* PBXTargetDependency */,
+ );
+ name = "metadata-prepare";
+ productName = "metadata-version";
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 00CC77691524DC17004886ED /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 00CC77681524DC17004886ED /* version.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 00F5358B155367740069AC35 /* ArtworkManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00F53589155367740069AC35 /* ArtworkManager.cpp */; };
+ 00F5358C155367740069AC35 /* ArtworkManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 00F5358A155367740069AC35 /* ArtworkManager.h */; };
+ B152EBD014F540B0005076BA /* api.h in Headers */ = {isa = PBXBuildFile; fileRef = B152EBC914F540B0005076BA /* api.h */; };
+ B152EBD114F540B0005076BA /* metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B152EBCA14F540B0005076BA /* metadata.cpp */; };
+ B152EBD214F540B0005076BA /* metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = B152EBCB14F540B0005076BA /* metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B152EBD514F540B0005076BA /* MetadataManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B152EBCE14F540B0005076BA /* MetadataManager.cpp */; };
+ B152EBD614F540B0005076BA /* MetadataManager.h in Headers */ = {isa = PBXBuildFile; fileRef = B152EBCF14F540B0005076BA /* MetadataManager.h */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 00CC77641524DA7D004886ED /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B152EBB914F54089005076BA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00CC775A1524DA3F004886ED;
+ remoteInfo = "metadata-version";
+ };
+ 00E6E7631548E8BA005C9962 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B152EBB914F54089005076BA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00CC775E1524DA4C004886ED;
+ remoteInfo = "metadata-cleanup";
+ };
+ 00E6E7651548E8C6005C9962 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B152EBB914F54089005076BA /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 00CC775E1524DA4C004886ED;
+ remoteInfo = "metadata-cleanup";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 00C27EA015374B78008D95CD /* precomp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = precomp.h; sourceTree = "<group>"; };
+ 00CC77681524DC17004886ED /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = version.h; path = $PROJECT_DERIVED_FILE_DIR/version.h; sourceTree = "<absolute>"; };
+ 00CC776A1524DC46004886ED /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
+ 00F53589155367740069AC35 /* ArtworkManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArtworkManager.cpp; sourceTree = "<group>"; };
+ 00F5358A155367740069AC35 /* ArtworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArtworkManager.h; sourceTree = "<group>"; };
+ B152EBC214F54089005076BA /* libmetadata.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmetadata.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ B152EBC914F540B0005076BA /* api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = api.h; sourceTree = "<group>"; };
+ B152EBCA14F540B0005076BA /* metadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = metadata.cpp; sourceTree = "<group>"; };
+ B152EBCB14F540B0005076BA /* metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadata.h; sourceTree = "<group>"; };
+ B152EBCE14F540B0005076BA /* MetadataManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MetadataManager.cpp; sourceTree = "<group>"; };
+ B152EBCF14F540B0005076BA /* MetadataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MetadataManager.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ B152EBBF14F54089005076BA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 00CC77591524DA29004886ED /* Version */ = {
+ isa = PBXGroup;
+ children = (
+ 00CC776A1524DC46004886ED /* VERSION */,
+ 00CC77681524DC17004886ED /* version.h */,
+ );
+ name = Version;
+ sourceTree = "<group>";
+ };
+ B152EBB714F54089005076BA = {
+ isa = PBXGroup;
+ children = (
+ 00F53589155367740069AC35 /* ArtworkManager.cpp */,
+ 00F5358A155367740069AC35 /* ArtworkManager.h */,
+ 00C27EA015374B78008D95CD /* precomp.h */,
+ B152EBC914F540B0005076BA /* api.h */,
+ B152EBCA14F540B0005076BA /* metadata.cpp */,
+ B152EBCB14F540B0005076BA /* metadata.h */,
+ B152EBCE14F540B0005076BA /* MetadataManager.cpp */,
+ B152EBCF14F540B0005076BA /* MetadataManager.h */,
+ 00CC77591524DA29004886ED /* Version */,
+ B152EBC314F54089005076BA /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ B152EBC314F54089005076BA /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ B152EBC214F54089005076BA /* libmetadata.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ B152EBC014F54089005076BA /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B152EBD014F540B0005076BA /* api.h in Headers */,
+ B152EBD214F540B0005076BA /* metadata.h in Headers */,
+ B152EBD614F540B0005076BA /* MetadataManager.h in Headers */,
+ 00CC77691524DC17004886ED /* version.h in Headers */,
+ 00F5358C155367740069AC35 /* ArtworkManager.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXLegacyTarget section */
+ 00CC775E1524DA4C004886ED /* metadata-cleanup */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(NSBUILD_TOOLS_BIN_DIR)/cleanbuild --xcode-mode --libraries \"$(LIBRARY_PATH)\" \"$(PUBLIC_HEADERS_DIR)\" \"$(DWARF_DSYM_PATH)\" \"$(PROJECT_DERIVED_FILE_DIR)/version.*\"";
+ buildConfigurationList = 00CC775F1524DA4C004886ED /* Build configuration list for PBXLegacyTarget "metadata-cleanup" */;
+ buildPhases = (
+ );
+ buildToolPath = /bin/sh;
+ buildWorkingDirectory = "";
+ dependencies = (
+ );
+ name = "metadata-cleanup";
+ passBuildSettingsInEnvironment = 1;
+ productName = "metadata-cleanup";
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ B152EBC114F54089005076BA /* metadata */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B152EBC614F54089005076BA /* Build configuration list for PBXNativeTarget "metadata" */;
+ buildPhases = (
+ B152EBBE14F54089005076BA /* Sources */,
+ B152EBBF14F54089005076BA /* Frameworks */,
+ B152EBC014F54089005076BA /* Headers */,
+ 00CC77661524DB1E004886ED /* Install Public Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 00E6E7661548E8C6005C9962 /* PBXTargetDependency */,
+ 00CC77651524DA7D004886ED /* PBXTargetDependency */,
+ );
+ name = metadata;
+ productName = "replicant-metadata";
+ productReference = B152EBC214F54089005076BA /* libmetadata.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ B152EBB914F54089005076BA /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0500;
+ ORGANIZATIONNAME = "Nullsoft, Inc.";
+ };
+ buildConfigurationList = B152EBBC14F54089005076BA /* Build configuration list for PBXProject "replicant-metadata" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = B152EBB714F54089005076BA;
+ productRefGroup = B152EBC314F54089005076BA /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ B152EBC114F54089005076BA /* metadata */,
+ 00CC775E1524DA4C004886ED /* metadata-cleanup */,
+ 00CC775A1524DA3F004886ED /* metadata-prepare */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00CC77661524DB1E004886ED /* Install Public Headers */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ name = "Install Public Headers";
+ outputPaths = (
+ "$(DSTROOT)$(PUBLIC_HEADERS_FOLDER_PATH)",
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "PARENTDIR=$(dirname \"$SCRIPT_INPUT_FILE_0\")\n\nif [ ! -L \"$PARENTDIR\" ]; then\n INSTALLTOOL=\"$NSBUILD_TOOLS_BIN_DIR/installtool\"\n $INSTALLTOOL --headers-only \\\n \"$SCRIPT_INPUT_FILE_0/\" \\\n \"$SCRIPT_OUTPUT_FILE_0\"\nfi\n";
+ showEnvVarsInLog = 0;
+ };
+ 00CC77671524DB6A004886ED /* Generate Version Info */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/VERSION",
+ "$(NSBUILD_TOOLS_SHARE_DIR)/nvgtool/lib-version.template.h",
+ );
+ name = "Generate Version Info";
+ outputPaths = (
+ "$(PROJECT_DERIVED_FILE_DIR)/version.h",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "PRODUCT_VERSION=$(cat \"$SCRIPT_INPUT_FILE_0\")\n\nif [ ! -d \"$PROJECT_DERIVED_FILE_DIR\" ]; then\n mkdir -p \"$PROJECT_DERIVED_FILE_DIR\"\nfi\n\nNVGTOOL=\"$NSBUILD_TOOLS_BIN_DIR/nvgtool\"\n$NVGTOOL --product-name \"replicant_$PRODUCT_NAME\" \\\n --product-version \"$PRODUCT_VERSION\" \\\n --input-file \"$SCRIPT_INPUT_FILE_1\" \\\n --output-file \"$SCRIPT_OUTPUT_FILE_0\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ B152EBBE14F54089005076BA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B152EBD114F540B0005076BA /* metadata.cpp in Sources */,
+ B152EBD514F540B0005076BA /* MetadataManager.cpp in Sources */,
+ 00F5358B155367740069AC35 /* ArtworkManager.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 00CC77651524DA7D004886ED /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00CC775A1524DA3F004886ED /* metadata-prepare */;
+ targetProxy = 00CC77641524DA7D004886ED /* PBXContainerItemProxy */;
+ };
+ 00E6E7641548E8BA005C9962 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00CC775E1524DA4C004886ED /* metadata-cleanup */;
+ targetProxy = 00E6E7631548E8BA005C9962 /* PBXContainerItemProxy */;
+ };
+ 00E6E7661548E8C6005C9962 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 00CC775E1524DA4C004886ED /* metadata-cleanup */;
+ targetProxy = 00E6E7651548E8C6005C9962 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 00CC775C1524DA3F004886ED /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ 00CC775D1524DA3F004886ED /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+ 00CC77601524DA4C004886ED /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Debug;
+ };
+ 00CC77611524DA4C004886ED /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ DWARF_DSYM_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME).dSYM";
+ LIBRARY_PATH = "$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_NAME)";
+ PUBLIC_HEADERS_DIR = "$(BUILT_PRODUCTS_DIR)$(PUBLIC_HEADERS_FOLDER_PATH)";
+ };
+ name = Release;
+ };
+ B152EBC414F54089005076BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = a;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib/Replicant";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACH_O_TYPE = staticlib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ ONLY_ACTIVE_ARCH = YES;
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/Replicant/$(PRODUCT_NAME)";
+ PRODUCT_NAME = metadata;
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/Replicant/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = "../.. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Debug;
+ };
+ B152EBC514F54089005076BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)";
+ EXECUTABLE_EXTENSION = a;
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME).$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = precomp.h;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INSTALL_PATH = "$(INSTALL_PATH_PREFIX)/lib/Replicant";
+ INSTALL_PATH_PREFIX = /usr/local;
+ MACH_O_TYPE = staticlib;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ NSBUILD_TOOLS_BIN_DIR = "$(NSBUILD_TOOLS_DIR)/bin";
+ NSBUILD_TOOLS_DIR = "$(SRCROOT)/../../../build-tools";
+ NSBUILD_TOOLS_SHARE_DIR = "$(NSBUILD_TOOLS_DIR)/share";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/Replicant/$(PRODUCT_NAME)";
+ PRODUCT_NAME = metadata;
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INSTALL_PATH_PREFIX)/include/Replicant/$(PRODUCT_NAME)";
+ SDKROOT = macosx;
+ USER_HEADER_SEARCH_PATHS = "../.. $(BUILT_PRODUCTS_DIR)$(INSTALL_PATH_PREFIX)/include";
+ };
+ name = Release;
+ };
+ B152EBC714F54089005076BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Debug;
+ };
+ B152EBC814F54089005076BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 00CC775B1524DA3F004886ED /* Build configuration list for PBXAggregateTarget "metadata-prepare" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00CC775C1524DA3F004886ED /* Debug */,
+ 00CC775D1524DA3F004886ED /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 00CC775F1524DA4C004886ED /* Build configuration list for PBXLegacyTarget "metadata-cleanup" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00CC77601524DA4C004886ED /* Debug */,
+ 00CC77611524DA4C004886ED /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B152EBBC14F54089005076BA /* Build configuration list for PBXProject "replicant-metadata" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B152EBC414F54089005076BA /* Debug */,
+ B152EBC514F54089005076BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B152EBC614F54089005076BA /* Build configuration list for PBXNativeTarget "metadata" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B152EBC714F54089005076BA /* Debug */,
+ B152EBC814F54089005076BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = B152EBB914F54089005076BA /* Project object */;
+}
diff --git a/Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/xcshareddata/xcschemes/replicant-metadata.xcscheme b/Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/xcshareddata/xcschemes/replicant-metadata.xcscheme
new file mode 100644
index 000000000..a0f5d7e2b
--- /dev/null
+++ b/Src/replicant/replicant/metadata/replicant-metadata.xcodeproj/xcshareddata/xcschemes/replicant-metadata.xcscheme
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0500"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "B152EBC114F54089005076BA"
+ BuildableName = "libmetadata.a"
+ BlueprintName = "metadata"
+ ReferencedContainer = "container:replicant-metadata.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ buildConfiguration = "Debug">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Debug"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Src/replicant/replicant/version.h b/Src/replicant/replicant/version.h
new file mode 100644
index 000000000..fade56a47
--- /dev/null
+++ b/Src/replicant/replicant/version.h
@@ -0,0 +1,9 @@
+#pragma once
+
+static const unsigned int replicant_build_number=1;
+static const char *replicant_version="1.1";
+
+/* for use in ifc_component's framework_guid field */
+// {E651D169-2465-4188-990B-3AA947B000D2}
+static const GUID replicant_platform_guid =
+{ 0xe651d169, 0x2465, 0x4188, { 0x99, 0xb, 0x3a, 0xa9, 0x47, 0xb0, 0x0, 0xd2 } };
diff --git a/Src/replicant/service/api_service.h b/Src/replicant/service/api_service.h
new file mode 100644
index 000000000..9c883f4c5
--- /dev/null
+++ b/Src/replicant/service/api_service.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#ifndef _API_SERVICE_H
+#define _API_SERVICE_H
+
+#include "../foundation/dispatch.h"
+#include "../foundation/types.h"
+#include "../foundation/guid.h"
+#include "../foundation/error.h"
+#include "../service/ifc_servicefactory.h"
+
+// ----------------------------------------------------------------------------
+// {6DD93387-6636-4479-B982-9FF5B91CF4B4}
+static const GUID service_api_service_guid =
+{ 0x6dd93387, 0x6636, 0x4479, { 0xb9, 0x82, 0x9f, 0xf5, 0xb9, 0x1c, 0xf4, 0xb4 } };
+
+class NOVTABLE api_service : public Wasabi2::Dispatchable
+{
+protected:
+ api_service() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_service() {}
+public:
+ static GUID GetServiceGUID() { return service_api_service_guid; }
+ int Register(ifc_serviceFactory *svc) { return Service_Register(svc); }
+ int Unregister(ifc_serviceFactory *svc) { return Service_Unregister(svc); }
+ size_t GetServiceCount(GUID svc_type) { return Service_GetServiceCount(svc_type); }
+ ifc_serviceFactory *EnumService(GUID svc_type, size_t n) { return Service_EnumService(svc_type, n); }
+ ifc_serviceFactory *EnumService(size_t n) { return Service_EnumService(n); }
+ ifc_serviceFactory *GetServiceByGUID(GUID guid) { return Service_GetServiceByGUID(guid); }
+
+ /* called by a component when it returns NErr_TryAgain from one of the quit-phase functions */
+ void ComponentDone() { Service_ComponentDone(); }
+
+ template <class _t>
+ int GetService(_t **out_service)
+ {
+ ifc_serviceFactory *sf = Service_GetServiceByGUID(_t::GetServiceGUID());
+ if (!sf)
+ return NErr_ServiceUnavailable;
+
+ _t *service = reinterpret_cast<_t *>(sf->GetInterface());
+ if (!service)
+ return NErr_FailedCreate;
+
+ *out_service = service;
+ return NErr_Success;
+ }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual int WASABICALL Service_Register(ifc_serviceFactory *svc)=0;
+ virtual int WASABICALL Service_Unregister(ifc_serviceFactory *svc)=0;
+ virtual size_t WASABICALL Service_GetServiceCount(GUID svc_type)=0;
+ virtual ifc_serviceFactory * WASABICALL Service_EnumService(GUID svc_type, size_t n)=0;
+ virtual ifc_serviceFactory * WASABICALL Service_EnumService(size_t n)=0;
+ virtual ifc_serviceFactory * WASABICALL Service_GetServiceByGUID(GUID guid)=0;
+ virtual void WASABICALL Service_ComponentDone()=0;
+};
+
+#endif // !_API_SERVICE_H \ No newline at end of file
diff --git a/Src/replicant/service/ifc_servicefactory.h b/Src/replicant/service/ifc_servicefactory.h
new file mode 100644
index 000000000..fa97665d8
--- /dev/null
+++ b/Src/replicant/service/ifc_servicefactory.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "../foundation/dispatch.h"
+#include "../foundation/guid.h"
+#include "../nx/nxstring.h"
+
+// ----------------------------------------------------------------------------
+
+class NOVTABLE ifc_serviceFactory : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_serviceFactory() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_serviceFactory() {}
+
+
+public:
+ GUID GetServiceType() { return ServiceFactory_GetServiceType(); }
+ nx_string_t GetServiceName() { return ServiceFactory_GetServiceName(); }
+ GUID GetGUID() { return ServiceFactory_GetGUID(); }
+ void *GetInterface() { return ServiceFactory_GetInterface(); }
+ int ServiceNotify(int msg, intptr_t param1 = 0, intptr_t param2 = 0) { return ServiceFactory_ServiceNotify(msg, param1, param2); }
+
+ // serviceNotify enums
+ enum
+ {
+ ONREGISTERED=0, // init yourself here -- not all other services are registered yet
+ ONSTARTUP=1, // everyone is initialized, safe to talk to other services
+ ONAPPRUNNING=2, // app is showing and processing events
+ ONBEFORESHUTDOWN=3, // system is about to shutdown, call WASABI2_API_APP->main_cancelShutdown() to cancel
+ ONSHUTDOWN=4, // studio is shutting down, release resources from other services
+ ONUNREGISTERED=5, // bye bye
+ };
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual GUID WASABICALL ServiceFactory_GetServiceType()=0;
+ virtual nx_string_t WASABICALL ServiceFactory_GetServiceName()=0;
+ virtual GUID WASABICALL ServiceFactory_GetGUID()=0;
+ virtual void *WASABICALL ServiceFactory_GetInterface()=0;
+ virtual int WASABICALL ServiceFactory_ServiceNotify(int msg, intptr_t param1 = 0, intptr_t param2 = 0)=0;
+};
diff --git a/Src/replicant/service/svccb.h b/Src/replicant/service/svccb.h
new file mode 100644
index 000000000..964c8097d
--- /dev/null
+++ b/Src/replicant/service/svccb.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "syscb/ifc_syscallback.h"
+#include "foundation/mkncc.h"
+
+namespace Service
+{
+ // {215CDE06-22A6-424F-9C64-DEDC45D84455}
+ static const GUID event_type =
+ { 0x215cde06, 0x22a6, 0x424f, { 0x9c, 0x64, 0xde, 0xdc, 0x45, 0xd8, 0x44, 0x55 } };
+ static const int on_register = 0;
+ static const int on_deregister = 1;
+
+ class SystemCallback : public ifc_sysCallback
+ {
+ protected:
+ GUID WASABICALL SysCallback_GetEventType() { return event_type; }
+ int WASABICALL SysCallback_Notify(int msg, intptr_t param1, intptr_t param2)
+ {
+ const GUID *service_id;
+
+ switch(msg)
+ {
+ case on_register:
+ service_id = (const GUID *)param1;
+ return ServiceSystemCallback_OnRegister(*service_id, (void *)param2);
+ case on_deregister:
+ service_id = (const GUID *)param1;
+ return ServiceSystemCallback_OnDeregister(*service_id, (void *)param2);
+ default:
+ return NErr_Success;
+ }
+ }
+ virtual int WASABICALL ServiceSystemCallback_OnRegister(GUID service_id, void *service) { return NErr_Success; }
+ virtual int WASABICALL ServiceSystemCallback_OnDeregister(GUID service_id, void *service) { return NErr_Success; }
+ };
+
+}
diff --git a/Src/replicant/service/types.h b/Src/replicant/service/types.h
new file mode 100644
index 000000000..7439edab6
--- /dev/null
+++ b/Src/replicant/service/types.h
@@ -0,0 +1,5 @@
+#pragma once
+#include "../foundation/guid.h"
+
+static const GUID SVC_TYPE_UNIQUE = { 0x3ffe7079, 0x2467, 0x495e, { 0x88, 0x18, 0x61, 0x64, 0x22, 0xb6, 0x51, 0x20 } };
+static const GUID SVC_TYPE_OBJECT = { 0x175b4de2, 0x8330, 0x48ce, { 0xb2, 0xab, 0xe9, 0x45, 0x49, 0x0b, 0x73, 0x14 } };
diff --git a/Src/replicant/ssdp/Android.mk b/Src/replicant/ssdp/Android.mk
new file mode 100644
index 000000000..659d0126f
--- /dev/null
+++ b/Src/replicant/ssdp/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+include $(ROOT_REPLICANT)/PlatformName.mk
+
+LOCAL_MODULE := ssdp
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE).$(PLATFORM_NAME).w6c
+
+LOCAL_C_INCLUDES := $(ROOT_REPLICANT)
+LOCAL_CFLAGS := -fvisibility=hidden
+
+LOCAL_STATIC_LIBRARIES := nu foundation
+LOCAL_SHARED_LIBRARIES := libjnet nx
+
+LOCAL_SRC_FILES := main.cpp SSDPAPI.cpp
+
+
+LOCAL_LDFLAGS = -llog
+include $(BUILD_SHARED_LIBRARY)
diff --git a/Src/replicant/ssdp/SSDPAPI.cpp b/Src/replicant/ssdp/SSDPAPI.cpp
new file mode 100644
index 000000000..b3574c838
--- /dev/null
+++ b/Src/replicant/ssdp/SSDPAPI.cpp
@@ -0,0 +1,436 @@
+#include "api__ssdp.h"
+#include "SSDPAPI.h"
+#include "foundation/error.h"
+#include "jnetlib/jnetlib.h"
+#include "nx/nxsleep.h"
+#include "nswasabi/AutoCharNX.h"
+#include "nswasabi/ReferenceCounted.h"
+#include <time.h>
+#include <stdio.h>
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+SSDPAPI::SSDPAPI()
+{
+ listener=0;
+ user_agent=0;
+}
+
+SSDPAPI::~SSDPAPI()
+{
+ jnl_udp_release(listener);
+
+ for (ServiceList::iterator itr=services.begin();itr!=services.end();itr++)
+ {
+ NXURIRelease(itr->location);
+ NXStringRelease(itr->type);
+ NXStringRelease(itr->usn);
+ }
+
+ for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
+ {
+ NXURIRelease(itr->location);
+ NXStringRelease(itr->type);
+ NXStringRelease(itr->usn);
+ }
+}
+
+int SSDPAPI::Initialize()
+{
+ user_agent=WASABI2_API_APP->GetUserAgent();
+ NXThreadCreate(&ssdp_thread, SSDPThread, this);
+ return NErr_Success;
+}
+
+int SSDPAPI::SSDP_RegisterService(nx_uri_t location, nx_string_t type, nx_string_t usn)
+{
+ nu::AutoLock auto_lock(services_lock);
+ Service service = {0};
+ service.location = NXURIRetain(location);
+ service.type = NXStringRetain(type);
+ service.usn = NXStringRetain(usn);
+ services.push_back(service);
+
+ return NErr_Success;
+}
+
+int SSDPAPI::SSDP_RegisterCallback(cb_ssdp *callback)
+{
+ if (!callback)
+ {
+ return NErr_BadParameter;
+ }
+
+ threadloop_node_t *node = thread_loop.GetAPC();
+ if (!node)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ node->func = APC_RegisterCallback;
+ node->param1 = this;
+ node->param2 = callback;
+ callback->Retain();
+ thread_loop.Schedule(node);
+ return NErr_Success;
+}
+
+int SSDPAPI::SSDP_UnregisterCallback(cb_ssdp *callback)
+{
+ if (!callback)
+ {
+ return NErr_BadParameter;
+ }
+
+ threadloop_node_t *node = thread_loop.GetAPC();
+ if (!node)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ node->func = APC_UnregisterCallback;
+ node->param1 = this;
+ node->param2 = callback;
+ // since we don't actually know if the callback is registered until the other thread runs
+ // we can't guarantee that we have a reference. so we'll add one!
+ callback->Retain();
+ thread_loop.Schedule(node);
+ return NErr_Success;
+}
+
+int SSDPAPI::SSDP_Search(nx_string_t type)
+{
+ if (!type)
+ {
+ return NErr_BadParameter;
+ }
+
+ threadloop_node_t *node = thread_loop.GetAPC();
+ if (!node)
+ {
+ return NErr_OutOfMemory;
+ }
+
+ node->func = APC_Search;
+ node->param1 = this;
+ node->param2 = NXStringRetain(type);
+ thread_loop.Schedule(node);
+ return NErr_Success;
+}
+
+int SSDPAPI::FindUSN(ServiceList &service_list, const char *usn, Service *&service)
+{
+ for (ServiceList::iterator itr = service_list.begin(); itr != service_list.end(); itr++)
+ {
+ if (!NXStringKeywordCompareWithCString(itr->usn, usn))
+ {
+ service = &(* itr);
+ return NErr_Success;
+ }
+ }
+
+ size_t num_services = service_list.size();
+ Service new_service = {0};
+ NXStringCreateWithUTF8(&new_service.usn, usn);
+ service_list.push_back(new_service);
+ service = &service_list.at(num_services);
+
+ return NErr_Empty;
+}
+
+nx_thread_return_t SSDPAPI::SSDPThread(nx_thread_parameter_t parameter)
+{
+ return ((SSDPAPI *)parameter)->Internal_Run();
+}
+
+void SSDPAPI::Internal_RegisterCallback(cb_ssdp *callback)
+{
+ callbacks.push_back(callback);
+ nu::AutoLock auto_lock(services_lock);
+ for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
+ {
+ callback->OnServiceConnected(itr->location, itr->type, itr->usn);
+ }
+}
+
+void SSDPAPI::Internal_UnregisterCallback(cb_ssdp *callback)
+{
+ // TODO: verify that our list actually contains the callback?
+ //callbacks.eraseObject(callback);
+ auto it = std::find(callbacks.begin(), callbacks.end(), callback);
+ if (it != callbacks.end())
+ {
+ callbacks.erase(it);
+ }
+
+ callback->Release(); // release the reference retained on the other thread
+ callback->Release(); // release _our_ reference
+}
+
+nx_thread_return_t SSDPAPI::Internal_Run()
+{
+ addrinfo *addr = 0;
+ jnl_dns_resolve_now("239.255.255.250", 1900, &addr, SOCK_DGRAM);
+ int ret = jnl_udp_create_multicast_listener(&listener, "239.255.255.250", 1900);
+ if (ret != NErr_Success || !addr)
+ return (nx_thread_return_t)ret;
+
+ jnl_httpu_request_t httpu;
+ ret = jnl_httpu_request_create(&httpu);
+ if (ret != NErr_Success)
+ {
+
+ return (nx_thread_return_t)ret;
+ }
+
+ time_t last_notify = 0;
+
+ for (;;)
+ {
+ time_t now = time(0);
+ if ((now - last_notify) > 15)
+ {
+ Internal_Notify(0, addr->ai_addr, (socklen_t)addr->ai_addrlen);
+ if (ret != NErr_Success)
+ {
+ // TODO: ?
+ }
+ last_notify = time(0);
+#if 0
+#if defined(_DEBUG) && defined(_WIN32)
+ FILE *f = fopen("c:/services.txt", "wb");
+ if (f)
+ {
+ for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
+ {
+
+ fprintf(f, "-----\r\n");
+ fprintf(f, "ST: %s\r\n", AutoCharPrintfUTF8(itr->type));
+ fprintf(f, "Location: %s\r\n", AutoCharPrintfUTF8(itr->location));
+ fprintf(f, "USN: %s\r\n", AutoCharPrintfUTF8(itr->usn));
+ }
+ fclose(f);
+ }
+#elif defined(__ANDROID__)
+ for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
+ {
+
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "-----\r\n");
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "ST: %s\r\n", AutoCharPrintfUTF8(itr->type));
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "Location: %s\r\n", AutoCharPrintfUTF8(itr->location));
+ __android_log_print(ANDROID_LOG_INFO, "libreplicant", "USN: %s\r\n", AutoCharPrintfUTF8(itr->usn));
+ }
+#endif
+#endif
+ }
+
+ thread_loop.Step(100);
+
+ for (;;)
+ {
+ size_t bytes_received=0;
+
+ jnl_udp_run(listener, 0, 8192, 0, &bytes_received);
+ if (bytes_received)
+ {
+ jnl_httpu_request_process(httpu, listener);
+ const char *method = jnl_httpu_request_get_method(httpu);
+ if (method)
+ {
+ if (!strcmp(method, "NOTIFY"))
+ {
+
+ unsigned int max_age=0;
+ const char *cache_control = jnl_httpu_request_get_header(httpu, "cache-control");
+ if (cache_control)
+ {
+ if (strncasecmp(cache_control, "max-age=", 8) == 0)
+ {
+ max_age = strtoul(cache_control+8, 0, 10);
+ }
+ }
+ const char *location = jnl_httpu_request_get_header(httpu, "location");
+ const char *nt = jnl_httpu_request_get_header(httpu, "nt");
+ const char *nts = jnl_httpu_request_get_header(httpu, "nts");
+ const char *usn = jnl_httpu_request_get_header(httpu, "usn");
+
+ // a hack to work with older android wifi sync protocol
+ if (!usn)
+ usn = jnl_httpu_request_get_header(httpu, "id");
+ if (usn && nt && location && nts && !strcmp(nts, "ssdp:alive"))
+ {
+ Service *service = 0;
+ int ret = FindUSN(found_services, usn, service);
+ if (ret == NErr_Success) // found an existing one
+ {
+ // update the last seen time and expiration date
+ // TODO: should we double check that the location didn't change?
+ bool changed=false;
+ ReferenceCountedNXString old_location;
+
+ if (service->location)
+ NXURIGetNXString(&old_location, service->location);
+ if (!service->location || NXStringKeywordCompareWithCString(old_location, location))
+ {
+ NXURIRelease(service->location);
+ NXURICreateWithUTF8(&service->location, location);
+ changed=true;
+ }
+
+ service->last_seen = time(0);
+ if (max_age)
+ service->expiry = service->last_seen + max_age;
+ else
+ service->expiry = (time_t)-1;
+
+ if (changed)
+ {
+ // notify clients
+ for (CallbackList::iterator itr=callbacks.begin();itr!=callbacks.end();itr++)
+ {
+ (*itr)->OnServiceDisconnected(service->usn);
+ (*itr)->OnServiceConnected(service->location, service->type, service->usn);
+ }
+ }
+ }
+ else if (ret == NErr_Empty) // new one
+ {
+ NXURICreateWithUTF8(&service->location, location);
+ NXStringCreateWithUTF8(&service->type, nt);
+ service->last_seen = time(0);
+ if (max_age)
+ service->expiry = service->last_seen + max_age;
+ else
+ service->expiry = (time_t)-1;
+
+ // notify clients
+ for (CallbackList::iterator itr=callbacks.begin();itr!=callbacks.end();itr++)
+ {
+ (*itr)->OnServiceConnected(service->location, service->type, service->usn);
+ }
+ }
+ }
+ }
+ else if (!strcmp(method, "M-SEARCH"))
+ {
+ sockaddr *addr; socklen_t addr_length;
+ jnl_udp_get_address(listener, &addr, &addr_length);
+ const char *st = jnl_httpu_request_get_header(httpu, "st");
+ Internal_Notify(st, addr, addr_length);
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // check for expirations
+again:
+ nu::AutoLock auto_lock(services_lock);
+ for (ServiceList::iterator itr=found_services.begin();itr!=found_services.end();itr++)
+ {
+ if (itr->expiry < now)
+ {
+ for (CallbackList::iterator itr2=callbacks.begin();itr2!=callbacks.end();itr2++)
+ {
+ (*itr2)->OnServiceDisconnected(itr->usn);
+ }
+
+ NXURIRelease(itr->location);
+ NXStringRelease(itr->usn);
+ NXStringRelease(itr->type);
+ found_services.erase(itr);
+ goto again;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+static char notify_request[] = "NOTIFY * HTTP/1.1\r\n";
+static char notify_host[] = "HOST: 239.255.255.250:1900\r\n";
+static char notify_cache_control[] = "CACHE-CONTROL:max-age=600\r\n";
+static char notify_nts[] = "NTS:ssdp:alive\r\n";
+
+static char search_request[] = "NOTIFY * HTTP/1.1\r\n";
+static char search_man[] = "MAN:\"ssdp:discover\"\r\n";
+
+ns_error_t SSDPAPI::Internal_Notify(const char *st, sockaddr *addr, socklen_t addr_length)
+{
+ jnl_udp_set_peer_address(listener, addr, addr_length);
+ nu::AutoLock auto_lock(services_lock);
+ for (ServiceList::iterator itr=services.begin();itr!=services.end();itr++)
+ {
+ if (st && NXStringKeywordCompareWithCString(itr->type, st))
+ continue;
+
+ jnl_udp_send(listener, notify_request, strlen(notify_request));
+ jnl_udp_send(listener, notify_host, strlen(notify_host));
+ jnl_udp_send(listener, notify_cache_control, strlen(notify_cache_control));
+ jnl_udp_send(listener, notify_nts, strlen(notify_nts));
+ char header[512] = {0};
+ snprintf(header, 511, "LOCATION:%s\r\n", AutoCharPrintfUTF8(itr->location));
+ header[511]=0;
+ jnl_udp_send(listener, header, strlen(header));
+ if (itr->usn)
+ {
+ snprintf(header, 511, "USN:%s\r\n", AutoCharPrintfUTF8(itr->usn));
+ header[511]=0;
+ jnl_udp_send(listener, header, strlen(header));
+ }
+ if (itr->type)
+ {
+ snprintf(header, 511, "NT:%s\r\n", AutoCharPrintfUTF8(itr->type));
+ header[511]=0;
+ jnl_udp_send(listener, header, strlen(header));
+ }
+ if (user_agent)
+ {
+ snprintf(header, 511, "SERVER:%s\r\n",user_agent);
+ header[511]=0;
+ jnl_udp_send(listener, header, strlen(header));
+ }
+ jnl_udp_send(listener, "\r\n", 2);
+
+ size_t bytes_sent=0;
+ do
+ {
+ jnl_udp_run(listener, 8192, 0, &bytes_sent, 0); // TODO: error check
+ if (bytes_sent == 0)
+ NXSleep(100);
+ } while (bytes_sent == 0);
+ }
+ return NErr_Success;
+}
+
+void SSDPAPI::Internal_Search(nx_string_t st)
+{
+ addrinfo *addr;
+ jnl_dns_resolve_now("239.255.255.250", 1900, &addr, SOCK_DGRAM);
+ jnl_udp_set_peer_address(listener, addr->ai_addr, (socklen_t)addr->ai_addrlen);
+ jnl_udp_send(listener, notify_request, strlen(notify_request));
+
+ jnl_udp_send(listener, search_request, strlen(search_request));
+ jnl_udp_send(listener, notify_host, strlen(notify_host));
+ jnl_udp_send(listener, search_man, strlen(search_man));
+ char header[512] = {0};
+ sprintf(header, "ST:%s\r\n", AutoCharPrintfUTF8(st));
+ header[511]=0;
+ jnl_udp_send(listener, header, strlen(header));
+ size_t bytes_sent=0;
+ do
+ {
+ jnl_udp_run(listener, 8192, 0, &bytes_sent, 0); // TODO: error check
+ if (bytes_sent == 0)
+ NXSleep(100);
+ } while (bytes_sent == 0);
+}
diff --git a/Src/replicant/ssdp/SSDPAPI.h b/Src/replicant/ssdp/SSDPAPI.h
new file mode 100644
index 000000000..d4d37f138
--- /dev/null
+++ b/Src/replicant/ssdp/SSDPAPI.h
@@ -0,0 +1,61 @@
+#pragma once
+#include "nx/nxstring.h"
+#include "nx/nxuri.h"
+#include <vector>
+#include "nu/AutoLock.h"
+#include "nx/nxthread.h"
+#include "foundation/error.h"
+#include "jnetlib/jnetlib.h"
+#include "cb_ssdp.h"
+#include "nu/ThreadLoop.h"
+#include "api_ssdp.h"
+#include "nswasabi/ServiceName.h"
+
+class SSDPAPI : public api_ssdp
+{
+public:
+ SSDPAPI();
+ ~SSDPAPI();
+ WASABI_SERVICE_NAME("SSDP API");
+ int Initialize();
+
+ int WASABICALL SSDP_RegisterService(nx_uri_t location, nx_string_t st, nx_string_t usn);
+ int WASABICALL SSDP_RegisterCallback(cb_ssdp *callback);
+ int WASABICALL SSDP_UnregisterCallback(cb_ssdp *callback);
+ int WASABICALL SSDP_Search(nx_string_t st);
+private:
+ struct Service
+ {
+ nx_uri_t location;
+ nx_string_t type;
+ nx_string_t usn;
+ time_t last_seen;
+ time_t expiry;
+ };
+
+ // TODO: map it off of USN
+ typedef std::vector<Service> ServiceList;
+ typedef std::vector<cb_ssdp*> CallbackList;
+ nx_thread_return_t Internal_Run();
+ ns_error_t Internal_Notify(const char *st, sockaddr *addr, socklen_t length);
+ void Internal_RegisterCallback(cb_ssdp *callback);
+ void Internal_UnregisterCallback(cb_ssdp *callback);
+ void Internal_Search(nx_string_t st);
+ static nx_thread_return_t NXTHREADCALL SSDPThread(nx_thread_parameter_t parameter);
+ int FindUSN(ServiceList &service_list, const char *usn, Service *&service);
+
+ ThreadLoop thread_loop;
+ CallbackList callbacks;
+ ServiceList services;
+ ServiceList found_services;
+ nu::LockGuard services_lock;
+ nx_thread_t ssdp_thread;
+ jnl_udp_t listener;
+ const char *user_agent;
+
+
+
+ static void APC_RegisterCallback(void *_this, void *_callback, double) { ((SSDPAPI *)_this)->Internal_RegisterCallback((cb_ssdp *)_callback); }
+ static void APC_UnregisterCallback(void *_this, void *_callback, double) { ((SSDPAPI *)_this)->Internal_UnregisterCallback((cb_ssdp *)_callback); }
+ static void APC_Search(void *_this, void *st, double) { ((SSDPAPI *)_this)->Internal_Search((nx_string_t)st); }
+};
diff --git a/Src/replicant/ssdp/api__ssdp.h b/Src/replicant/ssdp/api__ssdp.h
new file mode 100644
index 000000000..a3b1568dd
--- /dev/null
+++ b/Src/replicant/ssdp/api__ssdp.h
@@ -0,0 +1,20 @@
+#pragma once
+#include <stdio.h>
+#include <stdlib.h>
+#include <foundation/types.h>
+
+#include "service/api_service.h"
+extern api_service *serviceManager;
+#define WASABI2_API_SVC serviceManager
+
+#include "application/api_application.h"
+extern api_application *applicationApi;
+#define WASABI2_API_APP applicationApi
+
+#include "syscb/api_syscb.h"
+extern api_syscb *syscbApi;
+#define WASABI2_API_SYSCB syscbApi
+
+//#include "application/api_android.h"
+//extern api_android *androidApi;
+//#define WASABI2_API_ANDROID androidApi
diff --git a/Src/replicant/ssdp/api_ssdp.h b/Src/replicant/ssdp/api_ssdp.h
new file mode 100644
index 000000000..adf25ea41
--- /dev/null
+++ b/Src/replicant/ssdp/api_ssdp.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "service/types.h"
+#include "nx/nxuri.h"
+#include "nx/nxstring.h"
+#include "cb_ssdp.h"
+
+// {A2EF43B6-5044-4D44-AE38-71C2CE20587B}
+static const GUID ssdp_api_service_guid =
+{ 0xa2ef43b6, 0x5044, 0x4d44, { 0xae, 0x38, 0x71, 0xc2, 0xce, 0x20, 0x58, 0x7b } };
+
+class api_ssdp : public Wasabi2::Dispatchable
+{
+protected:
+ api_ssdp() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_ssdp() {}
+public:
+ static GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+ static GUID GetServiceGUID() { return ssdp_api_service_guid; }
+ int RegisterService(nx_uri_t location, nx_string_t st, nx_string_t usn) { return SSDP_RegisterService(location, st, usn); }
+ int RegisterCallback(cb_ssdp *callback) { return SSDP_RegisterCallback(callback); }
+ int UnregisterCallback(cb_ssdp *callback) { return SSDP_UnregisterCallback(callback); }
+ int Search(nx_string_t st) { return SSDP_Search(st); }
+private:
+ virtual int WASABICALL SSDP_RegisterService(nx_uri_t location, nx_string_t st, nx_string_t usn)=0;
+ virtual int WASABICALL SSDP_RegisterCallback(cb_ssdp *callback)=0;
+ virtual int WASABICALL SSDP_UnregisterCallback(cb_ssdp *callback)=0;
+ virtual int WASABICALL SSDP_Search(nx_string_t st)=0;
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+}; \ No newline at end of file
diff --git a/Src/replicant/ssdp/cb_ssdp.h b/Src/replicant/ssdp/cb_ssdp.h
new file mode 100644
index 000000000..75e249789
--- /dev/null
+++ b/Src/replicant/ssdp/cb_ssdp.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+class cb_ssdp : public Wasabi2::Dispatchable
+{
+protected:
+ cb_ssdp() : Wasabi2::Dispatchable(DISPATCHABLE_VERSION) {}
+ ~cb_ssdp() {}
+
+public:
+ void OnServiceConnected(nx_uri_t location, nx_string_t type, nx_string_t usn) { return SSDPCallback_OnServiceConnected(location, type, usn); }
+ void OnServiceDisconnected(nx_string_t usn) { return SSDPCallback_OnServiceDisconnected(usn); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+private:
+ virtual void WASABICALL SSDPCallback_OnServiceConnected(nx_uri_t location, nx_string_t type, nx_string_t usn)=0;
+ virtual void WASABICALL SSDPCallback_OnServiceDisconnected(nx_string_t usn)=0;
+}; \ No newline at end of file
diff --git a/Src/replicant/ssdp/main.cpp b/Src/replicant/ssdp/main.cpp
new file mode 100644
index 000000000..e3d2ff675
--- /dev/null
+++ b/Src/replicant/ssdp/main.cpp
@@ -0,0 +1,88 @@
+#include "api__ssdp.h"
+#include "foundation/export.h"
+#include "component/ifc_component.h"
+#include "nx/nxuri.h"
+#include "nswasabi/ReferenceCounted.h"
+#include "nswasabi/singleton.h"
+#include "SSDPAPI.h"
+
+api_service *WASABI2_API_SVC=0;
+api_application *WASABI2_API_APP=0;
+api_syscb *WASABI2_API_SYSCB=0;
+//api_android *WASABI2_API_ANDROID=0;
+
+SSDPAPI ssdpapi;
+static SingletonServiceFactory<SSDPAPI, api_ssdp> ssdp_factory;
+
+
+// {F47F99AB-F90F-4623-B8BE-454555A8FC79}
+static const GUID ssdp_component_guid =
+{ 0xf47f99ab, 0xf90f, 0x4623, { 0xb8, 0xbe, 0x45, 0x45, 0x55, 0xa8, 0xfc, 0x79 } };
+
+
+class SSDPComponent : public ifc_component
+{
+public:
+ SSDPComponent() : ifc_component(ssdp_component_guid) {}
+ int WASABICALL Component_Initialize(api_service *service);
+ int WASABICALL Component_RegisterServices(api_service *service);
+ void WASABICALL Component_DeregisterServices(api_service *service);
+ int WASABICALL Component_Quit(api_service *service);
+};
+
+
+int SSDPComponent::Component_Initialize(api_service *service)
+{
+ WASABI2_API_SVC = service;
+
+ int ret = jnl_init();
+ if (ret != NErr_Success)
+ {
+ return ret;
+ }
+ return NErr_Success;
+}
+
+int SSDPComponent::Component_RegisterServices(api_service *service)
+{
+ WASABI2_API_SVC = service;
+ if (WASABI2_API_SVC)
+ {
+ WASABI2_API_SVC->GetService(&WASABI2_API_APP);
+ WASABI2_API_SVC->GetService(&WASABI2_API_SYSCB);
+ //WASABI2_API_SVC->GetService(&WASABI2_API_ANDROID);
+ }
+
+ ssdpapi.Initialize();
+ ssdp_factory.Register(WASABI2_API_SVC, &ssdpapi);
+ return NErr_Success;
+}
+
+void SSDPComponent::Component_DeregisterServices(api_service *service)
+{
+ if (WASABI2_API_SVC)
+ WASABI2_API_SVC->Release();
+
+ if (WASABI2_API_APP)
+ WASABI2_API_APP->Release();
+
+ if (WASABI2_API_SYSCB)
+ WASABI2_API_SYSCB->Release();
+
+ //if (WASABI2_API_ANDROID)
+ // WASABI2_API_ANDROID->Release();
+
+ ssdp_factory.Deregister(WASABI2_API_SVC);
+}
+
+int SSDPComponent::Component_Quit(api_service *_service_manager)
+{
+ jnl_quit();
+ return NErr_Success;
+}
+
+static SSDPComponent component;
+extern "C" DLLEXPORT ifc_component *GetWasabi2Component()
+{
+ return &component;
+}
diff --git a/Src/replicant/ssdp/main.h b/Src/replicant/ssdp/main.h
new file mode 100644
index 000000000..6f70f09be
--- /dev/null
+++ b/Src/replicant/ssdp/main.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/Src/replicant/ssdp/resource.h b/Src/replicant/ssdp/resource.h
new file mode 100644
index 000000000..96878ea22
--- /dev/null
+++ b/Src/replicant/ssdp/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ssdp.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/replicant/ssdp/ssdp.rc b/Src/replicant/ssdp/ssdp.rc
new file mode 100644
index 000000000..fcff77115
--- /dev/null
+++ b/Src/replicant/ssdp/ssdp.rc
@@ -0,0 +1,76 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/replicant/ssdp/ssdp.vcxproj b/Src/replicant/ssdp/ssdp.vcxproj
new file mode 100644
index 000000000..1c55a8dcd
--- /dev/null
+++ b/Src/replicant/ssdp/ssdp.vcxproj
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>17.0</VCProjectVersion>
+ <ProjectGuid>{05DA3583-A0EC-4BD3-AF29-9703289F033C}</ProjectGuid>
+ <RootNamespace>ssdp</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>17.0.32203.90</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <TargetExt>.w6c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <TargetExt>.w6c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <TargetExt>.w6c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ <TargetExt>.w6c</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN32;_DEBUG;_WINDOWS;_USRDLL;SSDP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(ProjectName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Components\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Components\</Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN32;NDEBUG;_WINDOWS;_USRDLL;SSDP_EXPORTS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(ProjectName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Components\</Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN32;WIN64;_DEBUG;_WINDOWS;_USRDLL;SSDP_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(ProjectName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Components\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Components\</Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_UNICODE;UNICODE;WIN32;WIN64;NDEBUG;_WINDOWS;_USRDLL;SSDP_EXPORTS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)$(ProjectName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(ProjectName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Components\</Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\jnetlib\jnetlib.vcxproj">
+ <Project>{e105a0a2-7391-47c5-86ac-718003524c3d}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\nu\nu.vcxproj">
+ <Project>{f1f5cd60-0d5b-4cea-9eeb-2f87ff9aa915}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ <ProjectReference Include="..\nx\nx.vcxproj">
+ <Project>{57c90706-b25d-4aca-9b33-95cdb2427c27}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="SSDPAPI.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__ssdp.h" />
+ <ClInclude Include="api_ssdp.h" />
+ <ClInclude Include="cb_ssdp.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="SSDPAPI.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ssdp.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/ssdp/ssdp.vcxproj.filters b/Src/replicant/ssdp/ssdp.vcxproj.filters
new file mode 100644
index 000000000..dea8193ad
--- /dev/null
+++ b/Src/replicant/ssdp/ssdp.vcxproj.filters
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{82598013-881a-4b89-82bf-dc514bcc744a}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SSDPAPI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__ssdp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api_ssdp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="cb_ssdp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SSDPAPI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Resource Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ssdp.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/replicant/ssdp/version.rc2 b/Src/replicant/ssdp/version.rc2
new file mode 100644
index 000000000..59f23d287
--- /dev/null
+++ b/Src/replicant/ssdp/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION WINAMP_PRODUCTVER
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp 5.x Replicant Component"
+ VALUE "FileVersion", STR_WINAMP_PRODUCTVER
+ VALUE "InternalName", "ssdp.w6c"
+ VALUE "LegalCopyright", "Copyright © 2012-2014 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "ssdp.w6c"
+ VALUE "ProductName", "Winamp SSDP API Service"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/replicant/syscb/api_syscb.h b/Src/replicant/syscb/api_syscb.h
new file mode 100644
index 000000000..8f36d10cf
--- /dev/null
+++ b/Src/replicant/syscb/api_syscb.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "foundation/dispatch.h"
+#include "foundation/types.h"
+#include "service/types.h"
+
+class ifc_sysCallback;
+
+// {57B7A1B6-700E-44ff-9CB0-70B92BAF3959}
+static const GUID syscbApiServiceGuid =
+{ 0x57b7a1b6, 0x700e, 0x44ff, { 0x9c, 0xb0, 0x70, 0xb9, 0x2b, 0xaf, 0x39, 0x59 } };
+
+// ----------------------------------------------------------------------------
+
+class NOVTABLE api_syscb : public Wasabi2::Dispatchable
+{
+protected:
+ api_syscb() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~api_syscb() {}
+public:
+ static const GUID GetServiceGUID() { return syscbApiServiceGuid; }
+ static const GUID GetServiceType() { return SVC_TYPE_UNIQUE; }
+
+ int RegisterCallback(ifc_sysCallback *cb) { return SysCallbacks_RegisterCallback(cb); }
+ int UnregisterCallback(ifc_sysCallback *cb) { return SysCallbacks_UnregisterCallback(cb); }
+ int IssueCallback(GUID eventtype, int msg, intptr_t param1 = 0, intptr_t param2 = 0) { return SysCallbacks_IssueCallback(eventtype, msg, param1, param2); }
+
+ /** pass eventtype == 0 to enumerate all syscallbacks
+ ** call Release() on the returned ifc_sysCallback when you are done
+ ** although very few wasabi objects support this at this time (2 June 2008)
+ **/
+ ifc_sysCallback *syscb_enum(GUID eventtype, size_t n) { return SysCallbacks_Enum(eventtype, n); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual int WASABICALL SysCallbacks_RegisterCallback(ifc_sysCallback *cb)=0;
+ virtual int WASABICALL SysCallbacks_UnregisterCallback(ifc_sysCallback *cb)=0;
+ virtual int WASABICALL SysCallbacks_IssueCallback(GUID eventtype, int msg, intptr_t param1 = 0, intptr_t param2 = 0)=0;
+
+ /** pass eventtype == 0 to enumerate all syscallbacks
+ ** call Release() on the returned ifc_sysCallback when you are done
+ ** although very few wasabi objects support this at this time (2 June 2008)
+ **/
+ virtual ifc_sysCallback *WASABICALL SysCallbacks_Enum(GUID eventtype, size_t n)=0;
+
+
+};
+
diff --git a/Src/replicant/syscb/ifc_syscallback.h b/Src/replicant/syscb/ifc_syscallback.h
new file mode 100644
index 000000000..2575996de
--- /dev/null
+++ b/Src/replicant/syscb/ifc_syscallback.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "foundation/dispatch.h"
+#include "foundation/types.h"
+
+// ----------------------------------------------------------------------------
+
+class ifc_sysCallback : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_sysCallback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_sysCallback() {}
+public:
+
+public:
+ GUID GetEventType() { return SysCallback_GetEventType(); }
+ int Notify(int msg, intptr_t param1 = 0, intptr_t param2 = 0) { return SysCallback_Notify(msg, param1, param2); }
+
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+protected:
+ virtual GUID WASABICALL SysCallback_GetEventType()=0;
+ virtual int WASABICALL SysCallback_Notify(int msg, intptr_t param1, intptr_t param2)=0;
+};
+
diff --git a/Src/replicant/ultravox/ifc_ultravox_playback.h b/Src/replicant/ultravox/ifc_ultravox_playback.h
new file mode 100644
index 000000000..75f3b94cf
--- /dev/null
+++ b/Src/replicant/ultravox/ifc_ultravox_playback.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "jnetlib/jnetlib_defines.h"
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "player/svc_output.h"
+#include "player/ifc_player.h"
+#include "http/ifc_http.h"
+#include "ultravox/ifc_ultravox_reader.h"
+#include "player/ifc_playback_parameters.h"
+
+class ifc_ultravox_playback: public Wasabi2::Dispatchable
+{
+protected:
+ ifc_ultravox_playback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_ultravox_playback() {}
+public:
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+ int Run(ifc_http *http_parent, ifc_player *player, ifc_ultravox_reader *reader) { return UltravoxPlayback_Run(http_parent, player, reader); }
+
+protected:
+ virtual int WASABICALL UltravoxPlayback_Run(ifc_http *http_parent, ifc_player *player, ifc_ultravox_reader *reader)=0;
+};
diff --git a/Src/replicant/ultravox/ifc_ultravox_reader.h b/Src/replicant/ultravox/ifc_ultravox_reader.h
new file mode 100644
index 000000000..745eb1452
--- /dev/null
+++ b/Src/replicant/ultravox/ifc_ultravox_reader.h
@@ -0,0 +1,28 @@
+#pragma once
+#include "foundation/dispatch.h"
+
+class ifc_ultravox_reader : public Wasabi2::Dispatchable
+{
+protected:
+ ifc_ultravox_reader() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~ifc_ultravox_reader() {}
+public:
+ enum
+ {
+ DISPATCHABLE_VERSION=0,
+ };
+
+ size_t BytesBuffered() { return UltravoxReader_BytesBuffered(); }
+ int Read(void *buffer, size_t buffer_length, size_t *bytes_read) { return UltravoxReader_Read(buffer, buffer_length, bytes_read); }
+ int Peek(void *buffer, size_t buffer_length, size_t *bytes_read) { return UltravoxReader_Peek(buffer, buffer_length, bytes_read); }
+ int IsClosed() { return UltravoxReader_IsClosed(); }
+ /* gives you back the contents of exactly one packet. used when Ultravox is provided codec framing */
+ int ReadPacket(void *buffer, size_t buffer_length, size_t *bytes_read) { return UltravoxReader_ReadPacket(buffer, buffer_length, bytes_read); }
+private:
+ virtual int WASABICALL UltravoxReader_Read(void *buffer, size_t buffer_length, size_t *bytes_read)=0;
+ virtual int WASABICALL UltravoxReader_Peek(void *buffer, size_t buffer_length, size_t *bytes_read)=0;
+ virtual size_t WASABICALL UltravoxReader_BytesBuffered()=0;
+ virtual int WASABICALL UltravoxReader_IsClosed()=0;
+ virtual int WASABICALL UltravoxReader_ReadPacket(void *buffer, size_t buffer_length, size_t *bytes_read)=0;
+
+};
diff --git a/Src/replicant/ultravox/svc_ultravox_playback.h b/Src/replicant/ultravox/svc_ultravox_playback.h
new file mode 100644
index 000000000..2190d1cc9
--- /dev/null
+++ b/Src/replicant/ultravox/svc_ultravox_playback.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "jnetlib/jnetlib_defines.h"
+#include "foundation/dispatch.h"
+#include "foundation/error.h"
+#include "ultravox/ifc_ultravox_playback.h"
+// {CAA4D831-8387-4F7D-A752-6DD0759E9C09}
+static const GUID ultravox_playback_service_type_guid =
+{ 0xcaa4d831, 0x8387, 0x4f7d, { 0xa7, 0x52, 0x6d, 0xd0, 0x75, 0x9e, 0x9c, 0x9 } };
+
+
+class svc_ultravox_playback : public Wasabi2::Dispatchable
+{
+protected:
+ svc_ultravox_playback() : Dispatchable(DISPATCHABLE_VERSION) {}
+ ~svc_ultravox_playback() {}
+public:
+ static GUID GetServiceType() { return ultravox_playback_service_type_guid; }
+
+ NError CreatePlayback(jnl_http_t http, unsigned int classtype, ifc_ultravox_playback **playback) { return UltravoxPlaybackService_CreateDemuxer(http, classtype, playback); }
+ enum
+ {
+ DISPATCHABLE_VERSION,
+ };
+
+protected:
+ virtual NError WASABICALL UltravoxPlaybackService_CreateDemuxer(jnl_http_t http, unsigned int classtype, ifc_ultravox_playback **playback) = 0;
+};