aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Portable/pmp_njb
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Portable/pmp_njb
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Portable/pmp_njb')
-rw-r--r--Src/Plugins/Portable/pmp_njb/NJBDevice.cpp714
-rw-r--r--Src/Plugins/Portable/pmp_njb/NJBDevice.h168
-rw-r--r--Src/Plugins/Portable/pmp_njb/NOMAD DAP PC SDK v3_5.pdfbin0 -> 1469440 bytes
-rw-r--r--Src/Plugins/Portable/pmp_njb/Nmsdk.h922
-rw-r--r--Src/Plugins/Portable/pmp_njb/ctnmjb2.dllbin0 -> 126976 bytes
-rw-r--r--Src/Plugins/Portable/pmp_njb/main.cpp181
-rw-r--r--Src/Plugins/Portable/pmp_njb/pmp_njb.rc109
-rw-r--r--Src/Plugins/Portable/pmp_njb/pmp_njb.sln30
-rw-r--r--Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj315
-rw-r--r--Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj.filters61
-rw-r--r--Src/Plugins/Portable/pmp_njb/resource.h23
-rw-r--r--Src/Plugins/Portable/pmp_njb/resources/zenIcon.pngbin0 -> 238 bytes
-rw-r--r--Src/Plugins/Portable/pmp_njb/version.rc239
13 files changed, 2562 insertions, 0 deletions
diff --git a/Src/Plugins/Portable/pmp_njb/NJBDevice.cpp b/Src/Plugins/Portable/pmp_njb/NJBDevice.cpp
new file mode 100644
index 00000000..4eb0d41d
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/NJBDevice.cpp
@@ -0,0 +1,714 @@
+#include "NJBDevice.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+
+HWND CreateDummyWindow();
+extern HWND mainMessageWindow;
+
+static __int64 fileSize(wchar_t * filename)
+{
+ WIN32_FIND_DATA f={0};
+ HANDLE h = FindFirstFileW(filename,&f);
+ if(h == INVALID_HANDLE_VALUE) return -1;
+ FindClose(h);
+ ULARGE_INTEGER i;
+ i.HighPart = f.nFileSizeHigh;
+ i.LowPart = f.nFileSizeLow;
+ return i.QuadPart;
+}
+
+static void FillSongFromMeta(BYTE * buf,Song * song) {
+ BYTE * ptr = buf;
+ short count = 0;
+ short type = 0;
+ short NameLen = 0;
+ long DataLen = 0;
+ long lData;
+
+ memcpy(&count, ptr, sizeof(short));
+ ptr += sizeof(short);
+
+ for(int i=0; i<count; i++)
+ {
+ memcpy(&type, ptr, sizeof(short));
+ ptr += sizeof(short);
+ memcpy(&NameLen, ptr, sizeof(short));
+ ptr += sizeof(short);
+ memcpy(&DataLen, ptr, sizeof(long));
+ ptr += sizeof(long);
+
+ char itemname[MAX_PATH] = {0};
+ memcpy(itemname, ptr, NameLen);
+ itemname[NameLen]=0;
+
+ ptr += NameLen;
+
+ if(type == 1) { // binary
+ memcpy(&lData, ptr, min(DataLen,4));
+ if (!_stricmp(itemname,LENGTH)) song->length = lData * 1000;
+ else if (!_stricmp(itemname,FILESIZE)) song->size = lData;
+ else if (!_stricmp(itemname,TRACKNUM)) song->track = lData;
+ else if (!_stricmp(itemname,YEAR)) song->year = lData;
+ else if (!_stricmp(itemname,TRACKID)) song->trackid = lData;
+ } else if(type == 2) { // unicode
+ if (!_stricmp(itemname,TITLE)) lstrcpyn(song->title,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
+ else if (!_stricmp(itemname,ARTIST)) lstrcpyn(song->artist,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
+ else if (!_stricmp(itemname,ALBUM)) lstrcpyn(song->album,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
+ else if (!_stricmp(itemname,GENRE)) lstrcpyn(song->genre,(WCHAR*)ptr,min((DataLen+2)/2,fieldlen));
+ } else if(type == 0) { // ASCII
+ if (!_stricmp(itemname,CODEC)) {
+ int l=min(sizeof(song->codec)-1,DataLen);
+ memcpy(song->codec,ptr,l);
+ song->codec[l]=0;
+ }
+ }
+ ptr += DataLen;
+ }
+}
+
+static bool GetSong(DAPSDK_ID * item, long id, Song * song) {
+ long size;
+ if(m_pCTJukebox2->GetItemAttribute(id,(IUnknown*)item,0,&size,NULL) != S_OK) return false;
+ BYTE * buf = (BYTE*)calloc(size,sizeof(BYTE));
+ if(!buf) return false;
+ if(m_pCTJukebox2->GetItemAttribute(id,(IUnknown*)item,size,&size,(IUnknown*)buf) != S_OK) { free(buf); return false; }
+ FillSongFromMeta(buf,song);
+ free(buf);
+ return true;
+}
+
+static int song_sortfunc(const void *elem1, const void *elem2) {
+ Song *a=(Song *)*(void **)elem1;
+ Song *b=(Song *)*(void **)elem2;
+ return a->trackid - b->trackid;
+}
+
+static Song *BinaryChopFind(int id,Playlist * mpl) {
+ Song s;
+ s.trackid=id;
+ Song * d = &s;
+ Song ** ret = (Song**)bsearch(&d,mpl->songs.GetAll(),mpl->songs.GetSize(),sizeof(void*),song_sortfunc);
+ return ret?*ret:NULL;
+}
+
+static bool GetPlaylist(long id, DAPSDK_ID * item,Playlist * pl, Playlist * mpl)
+{
+ pl->dirty=false;
+ pl->plid = item->lID;
+ lstrcpyn(pl->name,item->bstrName,fieldlen);
+ SysFreeString(item->bstrName);
+ DAPSDK_ID song;
+ HRESULT hr = m_pCTJukebox2->FindFirstItem(id,(IUnknown*)item,(IUnknown*)&song);
+ while(hr == S_OK) {
+ Song * s = BinaryChopFind(song.lID,mpl);
+ if(s) pl->songs.Add(s);
+ hr = m_pCTJukebox2->FindNextItem(id,(IUnknown*)item,(IUnknown*)&song);
+ }
+ return true;
+}
+
+NJBDevice::NJBDevice(long id) : transcoder(NULL)
+{
+ InitializeCriticalSection(&csRevTransfer);
+ InitializeCriticalSection(&csTransfer);
+ devices.Add(this);
+
+ pmpDeviceLoading load;
+
+ load.dev = this;
+ load.UpdateCaption = NULL;
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)&load,PMP_IPC_DEVICELOADING);
+ if(load.UpdateCaption) {
+ load.UpdateCaption(WASABI_API_LNGSTRINGW(IDS_NJB_LOADING),load.context);
+ }
+
+ this->id = id;
+ transferQueueLength = 0;
+ messageWindow = CreateDummyWindow();
+ m_pCTJukebox2->SetCallbackWindow2(id,(long)messageWindow);
+
+ BYTE * ptr = NULL;
+ if(m_pCTJukebox2->GetDeviceProperties(id,kDeviceSerialNumberValue,(IUnknown*)ptr) == S_OK) {
+ memcpy(serial,ptr,16);
+ //free(ptr);
+ }
+
+ DAPSDK_ID item,parent,root;
+ Playlist * mpl = new Playlist;
+ BSTR name=NULL;
+ m_pCTJukebox2->GetDeviceProperties(id,kDeviceNameString,(IUnknown*)&name);
+ lstrcpyn(mpl->name,name?name:L"Creative Jukebox",fieldlen);
+ SysFreeString(name);
+
+ // search for tracks...
+ parent.lID = ALLTRACKSKEY;
+ parent.lType = kAudioTrackType;
+ HRESULT hr = m_pCTJukebox2->FindFirstItem(id,(IUnknown*)&parent,(IUnknown*)&item);
+
+ while(hr == S_OK) {
+ // add track
+ Song * song = new Song;
+ if(GetSong(&item,id,song)) {
+ mpl->songs.Add(song);
+ song->trackid = item.lID;
+ }
+ else delete song;
+ hr = m_pCTJukebox2->FindNextItem(id,(IUnknown*)&parent,(IUnknown*)&item);
+ }
+ qsort(mpl->songs.GetAll(),mpl->songs.GetSize(),sizeof(void*),song_sortfunc); // sort the master playlist by trackid, so we can find stuff later using binary chop
+ playlists.Add(mpl);
+
+ // search for playlists...
+ hr = m_pCTJukebox2->FindFirstRootItem(id,(IUnknown*)&root);
+ while(hr == S_OK) {
+ if(_wcsicmp(root.bstrName,L"PLAY LISTS")==0) {
+ playlistRoot.bstrName = L"PLAY LISTS";
+ playlistRoot.lID = root.lID;
+ playlistRoot.lType = root.lType;
+ HRESULT hr = m_pCTJukebox2->FindFirstParentItem(id,(IUnknown*)&root,(IUnknown*)&parent);
+ while(hr == S_OK) {
+ Playlist * pl = new Playlist;
+ if(GetPlaylist(id,&parent,pl,mpl)) playlists.Add(pl);
+ else delete pl;
+ hr = m_pCTJukebox2->FindNextParentItem(id,(IUnknown*)&root,(IUnknown*)&parent);
+ }
+ }
+ SysFreeString(root.bstrName);
+ hr = m_pCTJukebox2->FindNextRootItem(id,(IUnknown*)&root);
+ }
+
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICECONNECTED);
+ //transcoder = NULL;
+ transcoder = (Transcoder*)SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_GET_TRANSCODER);
+ if(transcoder) {
+ transcoder->AddAcceptableFormat(L"mp3");
+ //transcoder->AddAcceptableFormat(L"wav");
+ transcoder->AddAcceptableFormat(L"wma");
+ }
+}
+
+NJBDevice::~NJBDevice()
+{
+ m_pCTJukebox2->SetCallbackWindow2(id, (long)mainMessageWindow);
+ DestroyWindow(messageWindow);
+ messageWindow = NULL;
+ Playlist * mpl = (Playlist *)playlists.Get(0);
+ int l = mpl->songs.GetSize();
+ for(int i=0; i<l; i++) delete (Song *)mpl->songs.Get(i);
+ l = playlists.GetSize();
+ for(int i=0; i<l; i++) delete (Playlist *)playlists.Get(i);
+
+ for(int i=0; i<devices.GetSize(); i++) if(devices.Get(i) == this) { devices.Del(i); break; }
+ DeleteCriticalSection(&csRevTransfer);
+ DeleteCriticalSection(&csTransfer);
+ if(transcoder) SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(WPARAM)transcoder,PMP_IPC_RELEASE_TRANSCODER);
+}
+
+__int64 NJBDevice::getDeviceCapacityAvailable() {
+ DAPSDK_STORAGE_INFO s;
+ ULARGE_INTEGER ret;
+ m_pCTJukebox2->GetDeviceProperties(id,kStorageInfoStruct,(IUnknown*)&s);
+ ret.LowPart = s.freeL;
+ ret.HighPart = s.freeH;
+ return ret.QuadPart;
+}
+
+__int64 NJBDevice::getDeviceCapacityTotal() {
+ DAPSDK_STORAGE_INFO s;
+ ULARGE_INTEGER ret;
+ m_pCTJukebox2->GetDeviceProperties(id,kStorageInfoStruct,(IUnknown*)&s);
+ ret.LowPart = s.totalL;
+ ret.HighPart = s.totalH;
+ return ret.QuadPart;
+}
+
+void NJBDevice::Eject() {
+ Close();
+}
+
+void NJBDevice::Close()
+{
+ commitChanges();
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)this,PMP_IPC_DEVICEDISCONNECTED);
+ if(devices.GetSize() == 1)
+ m_pCTJukebox2->SetCallbackWindow2(0,(long)mainMessageWindow);
+ delete this;
+}
+
+static BYTE * setAttrib(BYTE * ptr,short type, char * name, BYTE * data, int datalen) {
+ short namelen = (short)strlen(name);
+ memcpy(ptr,&type,2); ptr += 2;
+ memcpy(ptr,&namelen,2); ptr += 2;
+ memcpy(ptr,&datalen,4); ptr += 4;
+ memcpy(ptr,name,namelen); ptr += namelen;
+ memcpy(ptr,data,datalen); ptr += datalen;
+ return ptr;
+}
+
+static BYTE * makeMetaFromItemRecord(itemRecordW * item, wchar_t * file, long * size) {
+ char codec[4]="WAV";
+ wchar_t * ext = wcsrchr(file,L'.') + 1;
+ if(!_wcsicmp(ext,L"mp3")) strncpy(codec,"MP3",3);
+ else if(!_wcsicmp(ext,L"wma")) strncpy(codec,"WMA",3);
+
+ if(!item->album) item->album = _wcsdup(L"");
+ if(!item->artist) item->artist = _wcsdup(L"");
+ if(!item->title) item->title = _wcsdup(L"");
+
+ *size = (long)(2/*count*/+2/*type*/+6/*namelen+datalen*/+strlen(TITLE)+2*wcslen(item->title)
+ +2+6+strlen(ALBUM)+2*wcslen(item->album)
+ +2+6+strlen(ARTIST)+2*wcslen(item->artist)
+ +2+6+strlen(CODEC)+strlen(codec)
+ +2+6+strlen(FILESIZE)+sizeof(long)
+ +2+6+strlen(LENGTH)+sizeof(long));
+
+ int count = 6;
+ if (item->year > 0 ){
+ *size+=2+6+(long)strlen(YEAR)+sizeof(short);
+ count++;
+ }
+ if (item->genre) {
+ *size+=(long)(2+6+strlen(GENRE)+2*wcslen(item->genre));
+ count++;
+ }
+ if (item->track>0) {
+ *size+= (long)(2+6+strlen(TRACKNUM)+sizeof(short));
+ count++;
+ }
+ BYTE *buf = (BYTE*)calloc(1,*size);
+ BYTE *ptr = buf;
+ memcpy(ptr, &count, sizeof(short));
+ ptr += sizeof(short);
+
+ ptr = setAttrib(ptr,2,TITLE,(BYTE*)((wchar_t*)(item->title)),(int)wcslen(item->title)*2);
+ ptr = setAttrib(ptr,2,ARTIST,(BYTE*)((wchar_t*)(item->artist)), (int)wcslen(item->artist)*2);
+ ptr = setAttrib(ptr,2,ALBUM,(BYTE*)((wchar_t*)(item->album)), (int)wcslen(item->album)*2);
+ if(item->genre) ptr = setAttrib(ptr,2,GENRE,(BYTE*)((wchar_t*)(item->genre)), (int)wcslen(item->genre)*2);
+ short v = item->track;
+ if(item->track>0) ptr = setAttrib(ptr,1,TRACKNUM,(BYTE*)&v,2);
+ v = item->year;
+ if(item->year>0) ptr = setAttrib(ptr,1,YEAR,(BYTE*)&v,2);
+ ptr = setAttrib(ptr,0,CODEC,(BYTE*)codec,(int)strlen(codec));
+ ptr = setAttrib(ptr,1,LENGTH,(BYTE*)&item->length,4);
+ __int64 filesize = fileSize(file);
+ ptr = setAttrib(ptr,1,FILESIZE,(BYTE*)&filesize,4);
+
+ return buf;
+}
+
+BOOL NJBDevice::WindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch(uMsg) {
+ case WM_USER: // start add item
+ if(transferItem.status == 0) {
+ long size;
+ BYTE * buf = makeMetaFromItemRecord(const_cast<itemRecordW *>(transferItem.track),transferItem.file,&size);
+ HRESULT hr = m_pCTJukebox2->AddItem(id,kAudioTrackType,SysAllocString(transferItem.file),size,(IUnknown*)buf);
+ transferItem.meta = buf;
+ if(hr != S_OK) this->WindowMessage(hwnd,WM_DAPSDK_ADDITEM_COMPLETE,-1,0);
+ }
+ break;
+ case WM_DAPSDK_ADDITEM_PROGRESS:
+ {
+ wchar_t buf[100] = {0};
+ wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING_PERCENT),(int)wParam);
+ transferItem.callback(transferItem.callbackContext,buf);
+ }
+ break;
+ case WM_DAPSDK_ADDITEM_COMPLETE:
+ if(wParam == 0) {
+ transferItem.callback(transferItem.callbackContext,WASABI_API_LNGSTRINGW(IDS_DONE));
+ Song * song = new Song;
+ song->trackid = (int)lParam;
+ FillSongFromMeta(transferItem.meta,song);
+ song->track = transferItem.track->track;
+ song->year = transferItem.track->year;
+ *transferItem.songid = (songid_t)song;
+ }
+ else {
+ transferItem.callback(transferItem.callbackContext,WASABI_API_LNGSTRINGW(IDS_ERROR));
+ }
+ transferItem.status = (wParam==0?1:2);
+ free(transferItem.meta);
+ break;
+ case WM_USER+1: // start get item
+ if(revTransferItem.status == 0) {
+ Song * song = (Song*)*revTransferItem.songid;
+ DAPSDK_ID item = {song->trackid,kAudioTrackType,song->title};
+ // memory allocated by SysAllocString is freed by COM (why, i don't know)
+ HRESULT hr = m_pCTJukebox2->GetItem(id,SysAllocString(revTransferItem.file),(IUnknown*)&item);
+ if(hr != S_OK) WindowMessage(hwnd,WM_DAPSDK_GETITEM_COMPLETE,-1,0);
+ }
+ break;
+ case WM_DAPSDK_GETITEM_PROGRESS:
+ {
+ wchar_t buf[100] = {0};
+ wsprintf(buf,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING_PERCENT),(int)wParam);
+ revTransferItem.callback(revTransferItem.callbackContext,buf);
+ }
+ break;
+ case WM_DAPSDK_GETITEM_COMPLETE:
+ revTransferItem.callback(revTransferItem.callbackContext,
+ WASABI_API_LNGSTRINGW((wParam==0?IDS_DONE:IDS_ERROR)));
+ revTransferItem.status = (wParam==0?1:2);
+ break;
+ }
+ return 0;
+}
+//p75
+int NJBDevice::transferTrackToDevice(const itemRecordW * track,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),songid_t * songid,int * killswitch) {
+ wchar_t file[2048] = {0};
+ wcsncpy(file,track->filename,2048);
+ bool deletefile = false;
+ if(transcoder) if(transcoder->ShouldTranscode(file)) {
+ wchar_t newfile[MAX_PATH] = {0};
+ wchar_t ext[10] = {0};
+ transcoder->CanTranscode(file,ext);
+ transcoder->GetTempFilePath(ext,newfile);
+ if(transcoder->TranscodeFile(file,newfile,killswitch,callback,callbackContext)) return -1;
+ wcsncpy(file,newfile,2048);
+ deletefile=true;
+ }
+ EnterCriticalSection(&csTransfer);
+ callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING));
+ transferItem.file = file;
+ transferItem.callback = callback;
+ transferItem.callbackContext = callbackContext;
+ transferItem.status=0; // in progress
+ transferItem.killswitch = killswitch;
+ transferItem.songid = songid;
+ transferItem.track = track;
+
+ //now start the transfer
+ PostMessage(messageWindow,WM_USER,0,0);
+
+ while(transferItem.status==0) Sleep(10); // wait for transfer
+
+ // transfer completed
+ int ret = transferItem.status==1?0:-1;
+
+ LeaveCriticalSection(&csTransfer);
+ if(deletefile) _wunlink(file);
+ return ret;
+}
+
+int NJBDevice::trackAddedToTransferQueue(const itemRecordW * track) {
+ __int64 l;
+ if(transcoder && transcoder->ShouldTranscode(track->filename)) {
+ int k = transcoder->CanTranscode(track->filename);
+ if(k == -1) return -2;
+ if(k == 0) l = fileSize(track->filename);
+ else l = (__int64)k;
+ } else {
+ wchar_t * ext = wcsrchr(track->filename,L'.');
+ if(!ext) return -2;
+ if(_wcsicmp(ext,L".mp3") && _wcsicmp(ext,L".wma") && _wcsicmp(ext,L".wav")) return -2;
+ l = fileSize(track->filename);
+ }
+ if(transferQueueLength + l + 1000000 > getDeviceCapacityAvailable())
+ return -1;
+ else {
+ transferQueueLength += l;
+ return 0;
+ }
+}
+
+void NJBDevice::trackRemovedFromTransferQueue(const itemRecordW * track) {
+ __int64 l = (__int64)fileSize(track->filename);
+ if(transcoder && transcoder->ShouldTranscode(track->filename)) {
+ int k = transcoder->CanTranscode(track->filename);
+ if(k != -1 && k != 0) l = (__int64)k;
+ }
+ transferQueueLength -= l;
+}
+
+__int64 NJBDevice::getTrackSizeOnDevice(const itemRecordW * track) {
+ if(transcoder && transcoder->ShouldTranscode(track->filename)) {
+ int k = transcoder->CanTranscode(track->filename);
+ if(k != -1 && k != 0) return k;
+ }
+ wchar_t * ext = wcsrchr(track->filename,L'.');
+ if(!ext) return 0;
+ if(_wcsicmp(ext,L".mp3") && _wcsicmp(ext,L".wma") && _wcsicmp(ext,L".wav")) return 0;
+ return fileSize(track->filename);
+}
+
+void NJBDevice::deleteTrack(songid_t songid) {
+ Song * s = (Song*)songid;
+ for(int i=0; i<playlists.GetSize(); i++) {
+ Playlist * pl = (Playlist *)playlists.Get(i);
+ int l = pl->songs.GetSize();
+ while(l-- > 0) if(pl->songs.Get(l) == (void*)s) { pl->songs.Del(l); if(i>0) pl->dirty=true; }
+ }
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->DeleteItem(id,(IUnknown*)&item);
+ delete s;
+}
+
+void NJBDevice::commitChanges() {
+ for(int i=1; i<playlists.GetSize(); i++) {
+ Playlist * pl = (Playlist *)playlists.Get(i);
+ if(pl->dirty) {
+ pl->dirty = false;
+ DAPSDK_ID parentold = {pl->plid,kPlaylistType,pl->name};
+ m_pCTJukebox2->DeleteParentItem(id,(IUnknown*)&parentold);
+ DAPSDK_ID parent = {0,kPlaylistType,_wcsdup(pl->name)};
+ m_pCTJukebox2->AddParentItem(id,(IUnknown*)&playlistRoot,(IUnknown*)&parent);
+ pl->plid = parent.lID;
+ long l = pl->songs.GetSize();
+ DAPSDK_ID * list = (DAPSDK_ID *)calloc(sizeof(DAPSDK_ID),l);
+ for(int j=0; j<l; j++) {
+ Song * s = (Song*)pl->songs.Get(j);
+ if(s) {
+ list[j].lID = s->trackid;
+ list[j].lType = kAudioTrackType;
+ list[j].bstrName = SysAllocString(s->title);
+ }
+ }
+ m_pCTJukebox2->AddItemsToParentItem(id,(IUnknown*)&parent,l,(IUnknown*)list);
+ free(list);
+ }
+ }
+}
+
+int NJBDevice::getPlaylistCount() {
+ return playlists.GetSize();
+}
+
+void NJBDevice::getPlaylistName(int playlistnumber, wchar_t * buf, int len) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ lstrcpyn(buf,pl->name,len);
+}
+
+int NJBDevice::getPlaylistLength(int playlistnumber) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ return pl->songs.GetSize();
+}
+
+songid_t NJBDevice::getPlaylistTrack(int playlistnumber,int songnum) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ return (songid_t) pl->songs.Get(songnum);
+}
+
+void NJBDevice::setPlaylistName(int playlistnumber, const wchar_t * buf) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ lstrcpyn(pl->name,buf,fieldlen);
+ DAPSDK_ID item = {pl->plid,kPlaylistType,pl->name};
+ BSTR name = SysAllocString(buf);
+ m_pCTJukebox2->RenameParentItem(id,(IUnknown*)&item, name);
+ SysFreeString(name);
+}
+
+void NJBDevice::playlistSwapItems(int playlistnumber, int posA, int posB) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ void * a = pl->songs.Get(posA);
+ void * b = pl->songs.Get(posB);
+ pl->songs.Set(posA,b);
+ pl->songs.Set(posB,a);
+ pl->dirty = true;
+}
+
+static int sortby;
+#define RETIFNZ(v) if ((v)!=0) return v;
+#define STRCMP_NULLOK _wcsicmp
+
+static int sortFunc(const void *elem1, const void *elem2)
+{
+ int use_by = sortby;
+ Song *a=(Song *)*(void **)elem1;
+ Song *b=(Song *)*(void **)elem2;
+
+ // this might be too slow, but it'd be nice
+ int x;
+ for (x = 0; x < 5; x ++)
+ {
+ if (use_by == SORTBY_TITLE) // title -> artist -> album -> disc -> track
+ {
+ int v=STRCMP_NULLOK(a->title,b->title);
+ RETIFNZ(v)
+ use_by=SORTBY_ARTIST;
+ }
+ else if (use_by == SORTBY_ARTIST) // artist -> album -> disc -> track -> title
+ {
+ int v=STRCMP_NULLOK(a->artist,b->artist);
+ RETIFNZ(v)
+ use_by=SORTBY_ALBUM;
+ }
+ else if (use_by == SORTBY_ALBUM) // album -> disc -> track -> title -> artist
+ {
+ int v=STRCMP_NULLOK(a->album,b->album);
+ RETIFNZ(v)
+ use_by=SORTBY_DISCNUM;
+ }
+ else if (use_by == SORTBY_TRACKNUM) // track -> title -> artist -> album -> disc
+ {
+ int v1=a->track;
+ int v2=b->track;
+ if (v1<0)v1=0;
+ if (v2<0)v2=0;
+ RETIFNZ(v1-v2)
+ use_by=SORTBY_TITLE;
+ }
+ else if (use_by == SORTBY_GENRE) // genre -> artist -> album -> disc -> track
+ {
+ int v=STRCMP_NULLOK(a->genre,b->genre);
+ RETIFNZ(v)
+ use_by=SORTBY_ARTIST;
+ }
+ else break; // no sort order?
+ }
+
+ return 0;
+}
+#undef RETIFNZ
+#undef STRCMP_NULLOK
+
+
+void NJBDevice::sortPlaylist(int playlistnumber, int sortBy) {
+ sortby = sortBy;
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ qsort(pl->songs.GetAll(),pl->songs.GetSize(),sizeof(void*),sortFunc);
+ pl->dirty=true;
+}
+
+void NJBDevice::addTrackToPlaylist(int playlistnumber, songid_t songid) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ pl->songs.Add((void*)songid);
+ pl->dirty = true;
+}
+
+void NJBDevice::removeTrackFromPlaylist(int playlistnumber, int songnum) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ pl->songs.Del(songnum);
+ pl->dirty = true;
+}
+
+void NJBDevice::deletePlaylist(int playlistnumber) {
+ Playlist * pl = (Playlist *)playlists.Get(playlistnumber);
+ DAPSDK_ID parent = {pl->plid,kPlaylistType,pl->name};
+ m_pCTJukebox2->DeleteParentItem(id,(IUnknown*)&parent);
+ playlists.Del(playlistnumber);
+ delete pl;
+}
+
+int NJBDevice::newPlaylist(const wchar_t * name) {
+ Playlist * pl = new Playlist;
+ pl->dirty = false;
+ lstrcpyn(pl->name,name,fieldlen);
+ DAPSDK_ID parent = {0,kPlaylistType,pl->name};
+ m_pCTJukebox2->AddParentItem(id,(IUnknown*)&playlistRoot,(IUnknown*)&parent);
+ pl->plid = parent.lID;
+ playlists.Add(pl);
+ return playlists.GetSize() - 1;
+}
+
+void NJBDevice::getTrackArtist(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->artist,len);}
+void NJBDevice::getTrackAlbum(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->album,len);}
+void NJBDevice::getTrackTitle(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->title,len);}
+void NJBDevice::getTrackGenre(songid_t songid, wchar_t * buf, int len) {lstrcpyn(buf,((Song*)songid)->genre,len);}
+int NJBDevice::getTrackTrackNum(songid_t songid) {return ((Song*)songid)->track;}
+int NJBDevice::getTrackDiscNum(songid_t songid) {return -1;}
+int NJBDevice::getTrackYear(songid_t songid) {return ((Song*)songid)->year;}
+__int64 NJBDevice::getTrackSize(songid_t songid) {return ((Song*)songid)->size;}
+int NJBDevice::getTrackLength(songid_t songid) {return ((Song*)songid)->length;}
+int NJBDevice::getTrackBitrate(songid_t songid) {return -1;}
+int NJBDevice::getTrackPlayCount(songid_t songid) {return -1;}
+int NJBDevice::getTrackRating(songid_t songid) {return -1;}
+__time64_t NJBDevice::getTrackLastPlayed(songid_t songid) {return -1;}
+__time64_t NJBDevice::getTrackLastUpdated(songid_t songid) {return -1;}
+
+void NJBDevice::getTrackExtraInfo(songid_t songid, const wchar_t * field, wchar_t * buf, int len) {
+ if(!wcscmp(field,L"ext")) {
+ Song * s = (Song *)songid;
+ lstrcpyn(buf,(wchar_t*)AutoWide(s->codec),len);
+ wchar_t * p = buf;
+ while(p && *p) { *p=towlower(*p); p++; }
+ }
+}
+
+void NJBDevice::setTrackArtist(songid_t songid, const wchar_t * value) {
+ Song * s = (Song *)songid;
+ lstrcpyn(s->artist,value,fieldlen);
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"ARTIST",2,(long)wcslen(value)*2+2,(IUnknown*)value);
+}
+
+void NJBDevice::setTrackAlbum(songid_t songid, const wchar_t * value) {
+ Song * s = (Song *)songid;
+ lstrcpyn(s->album,value,fieldlen);
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"ALBUM",2, (long)wcslen(value)*2+2,(IUnknown*)value);
+}
+
+
+void NJBDevice::setTrackTitle(songid_t songid, const wchar_t * value) {
+ Song * s = (Song *)songid;
+ lstrcpyn(s->title,value,fieldlen);
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"TITLE",2, (long)wcslen(value)*2+2,(IUnknown*)value);
+}
+
+
+void NJBDevice::setTrackGenre(songid_t songid, const wchar_t * value) {
+ Song * s = (Song *)songid;
+ lstrcpyn(s->genre,value,fieldlen);
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"GENRE",2, (long)wcslen(value)*2+2,(IUnknown*)value);
+}
+
+
+void NJBDevice::setTrackTrackNum(songid_t songid, int value) {
+ Song * s = (Song *)songid;
+ s->track = value;
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"TRACK NUM",1,sizeof(short),(IUnknown*)&value);
+}
+
+void NJBDevice::setTrackYear(songid_t songid, int value) {
+ Song * s = (Song *)songid;
+ s->year = value;
+ DAPSDK_ID item = {s->trackid,kAudioTrackType,s->title};
+ m_pCTJukebox2->SetItemAttribute(id,(IUnknown*)&item,L"YEAR",1,sizeof(short),(IUnknown*)&value);
+}
+
+int NJBDevice::copyToHardDrive(songid_t s,wchar_t * path,void * callbackContext,void (*callback)(void * callbackContext, wchar_t * status),int * killswitch) {
+ EnterCriticalSection(&csRevTransfer);
+
+ callback(callbackContext,WASABI_API_LNGSTRINGW(IDS_TRANSFERRING));
+ Song * song = (Song*)s;
+ wcscat(path,L".");
+ wcscat(path,AutoWide(song->codec));
+
+ wchar_t *p = wcsrchr(path,L'.');
+ while(p && *p) { *p = towlower(*p); p++; }
+
+ revTransferItem.callback = callback;
+ revTransferItem.callbackContext = callbackContext;
+ revTransferItem.killswitch = killswitch;
+ revTransferItem.songid = &s;
+ revTransferItem.file = path;
+ revTransferItem.status = 0;
+
+ PostMessage(messageWindow,WM_USER+1,0,0);
+
+ while(revTransferItem.status==0) Sleep(10); // wait for transfer
+
+ int ret = revTransferItem.status==1?0:-1;
+
+ LeaveCriticalSection(&csRevTransfer);
+ return ret;
+}
+
+intptr_t NJBDevice::extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4) {
+ switch(param1) {
+ case DEVICE_SET_ICON:
+ {
+ MLTREEIMAGE * i = (MLTREEIMAGE*)param2;
+ i->hinst = plugin.hDllInstance;
+ i->resourceId = IDR_ZEN_ICON;
+ }
+ break;
+ case DEVICE_SUPPORTED_METADATA:
+ return 0x3ef;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_njb/NJBDevice.h b/Src/Plugins/Portable/pmp_njb/NJBDevice.h
new file mode 100644
index 00000000..d42b4070
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/NJBDevice.h
@@ -0,0 +1,168 @@
+#ifndef _NJBDEVICE_H_
+#define _NJBDEVICE_H_
+
+#ifndef _UNICODE
+#define _UNICODE
+#endif
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <windows.h>
+#include <windowsx.h>
+#include <Objbase.h>
+#include <initguid.h>
+#include <stdio.h>
+#include <shlobj.h>
+#include <wchar.h>
+#include "../Plugins/General/gen_ml/ml.h"
+#include "../Plugins/Library/ml_pmp/pmp.h"
+#include "../Plugins/Library/ml_pmp/transcoder.h"
+#include "../Plugins/General/gen_ml/itemlist.h"
+#include "Nmsdk.h"
+#include "resource.h"
+
+#include "../Agave/Language/api_language.h"
+#include <api/service/waServiceFactory.h>
+
+extern PMPDevicePlugin plugin;
+extern LRESULT CALLBACK CallbackWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+extern C_ItemList devices;
+extern ICTJukebox2 * m_pCTJukebox2;
+
+#define fieldlen 256
+
+class Playlist {
+public:
+ bool dirty;
+ int plid;
+ wchar_t name[fieldlen];
+ C_ItemList songs;
+};
+
+class Song {
+public:
+ char codec[5];
+ int trackid;
+ wchar_t artist[fieldlen];
+ wchar_t album[fieldlen];
+ wchar_t title[fieldlen];
+ wchar_t genre[fieldlen];
+ int year,track,size,length;
+};
+
+class TransferItem {
+public:
+ const itemRecordW * track;
+ void* callbackContext;
+ void (*callback)(void * callbackContext, wchar_t * status);
+ songid_t * songid;
+ int * killswitch;
+ int status;
+ BYTE * meta;
+ wchar_t * file;
+};
+
+class NJBDevice : public Device {
+public:
+ Transcoder * transcoder;
+ BYTE serial[16];
+ __int64 transferQueueLength;
+ DAPSDK_ID playlistRoot;
+ C_ItemList playlists;
+ int id;
+ HWND messageWindow;
+ TransferItem transferItem;
+ TransferItem revTransferItem;
+ CRITICAL_SECTION csTransfer;
+ CRITICAL_SECTION csRevTransfer;
+
+ NJBDevice(long id);
+ virtual ~NJBDevice();
+
+ virtual BOOL WindowMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual __int64 getDeviceCapacityAvailable(); // in bytes
+ virtual __int64 getDeviceCapacityTotal(); // in bytes
+
+ virtual void Eject(); // if you ejected successfully, you MUST call plugin.deviceDisconnected(this) and delete this;
+ virtual void Close(); // save any changes, and call plugin.deviceDisconnected(this) AND delete this;
+
+
+ // return 0 for success, -1 for failed or cancelled
+ virtual int transferTrackToDevice(const itemRecordW * track, // the track to transfer
+ void * callbackContext, //pass this to the callback
+ void (*callback)(void * callbackContext, wchar_t * status), // call this every so often so the GUI can be updated. Including when finished!
+ songid_t * songid, // fill in the songid when you are finished
+ int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
+ );
+ virtual int trackAddedToTransferQueue(const itemRecordW * track); // return 0 to accept, -1 for "not enough space", -2 for "incorrect format"
+ virtual void trackRemovedFromTransferQueue(const itemRecordW * track);
+ virtual __int64 getTrackSizeOnDevice(const itemRecordW * track); // return the amount of space taken up on the device by the track, or 0 for incompatable (usually the filesize, unless you are transcoding)
+
+ virtual void deleteTrack(songid_t songid); // physically remove from device. Be sure to remove it from all the playlists!
+
+ virtual void commitChanges(); // optional. Will be called at a good time to save changes
+
+ virtual int getPlaylistCount(); // always at least 1. playlistnumber 0 is the Master Playlist containing all tracks.
+ // PlaylistName(0) should return the name of the device.
+ virtual void getPlaylistName(int playlistnumber, wchar_t * buf, int len);
+ virtual int getPlaylistLength(int playlistnumber);
+ virtual songid_t getPlaylistTrack(int playlistnumber,int songnum); // returns a songid
+
+ virtual void setPlaylistName(int playlistnumber, const wchar_t * buf); // with playlistnumber={}, set the name of the device.
+ virtual void playlistSwapItems(int playlistnumber, int posA, int posB); // swap the songs at position posA and posB
+ virtual void sortPlaylist(int playlistnumber, int sortBy); // only implement if you are a non-cached device!!!
+ virtual void addTrackToPlaylist(int playlistnumber, songid_t songid); // adds songid to the end of the playlist
+ virtual void removeTrackFromPlaylist(int playlistnumber, int songnum); //where songnum is the position of the track in the playlist
+
+ virtual void deletePlaylist(int playlistnumber);
+ virtual int newPlaylist(const wchar_t * name); // create empty playlist, returns playlistnumber
+
+ virtual void getTrackArtist(songid_t songid, wchar_t * buf, int len);
+ virtual void getTrackAlbum(songid_t songid, wchar_t * buf, int len);
+ virtual void getTrackTitle(songid_t songid, wchar_t * buf, int len);
+ virtual int getTrackTrackNum(songid_t songid);
+ virtual int getTrackDiscNum(songid_t songid);
+ virtual void getTrackGenre(songid_t songid, wchar_t * buf, int len);
+ virtual int getTrackYear(songid_t songid);
+ virtual __int64 getTrackSize(songid_t songid); // in bytes
+ virtual int getTrackLength(songid_t songid); // in millisecs
+ virtual int getTrackBitrate(songid_t songid); // in kbps
+ virtual int getTrackPlayCount(songid_t songid);
+ virtual int getTrackRating(songid_t songid); //0-5
+ virtual __time64_t getTrackLastPlayed(songid_t songid); // in unix time format
+ virtual __time64_t getTrackLastUpdated(songid_t songid); // in unix time format
+ virtual void getTrackExtraInfo(songid_t songid, const wchar_t * field, wchar_t * buf, int len); //optional
+
+ // feel free to ignore any you don't support
+ virtual void setTrackArtist(songid_t songid, const wchar_t * value);
+ virtual void setTrackAlbum(songid_t songid, const wchar_t * value);
+ virtual void setTrackTitle(songid_t songid, const wchar_t * value);
+ virtual void setTrackTrackNum(songid_t songid, int value);
+ virtual void setTrackDiscNum(songid_t songid, int value){};
+ virtual void setTrackGenre(songid_t songid, const wchar_t * value);
+ virtual void setTrackYear(songid_t songid, int year);
+ virtual void setTrackPlayCount(songid_t songid, int value){};
+ virtual void setTrackRating(songid_t songid, int value){};
+ virtual void setTrackLastPlayed(songid_t songid, __time64_t value){}; // in unix time format
+ virtual void setTrackLastUpdated(songid_t songid, __time64_t value){}; // in unix time format
+ virtual void setTrackExtraInfo(songid_t songid, const wchar_t * field, const wchar_t * value) {}; //optional
+
+ virtual bool playTracks(songid_t * songidList, int listLength, int startPlaybackAt, bool enqueue){return false;}; // return false if unsupported
+
+ virtual intptr_t extraActions(intptr_t param1, intptr_t param2, intptr_t param3,intptr_t param4);
+
+ virtual bool copyToHardDriveSupported() {return true;} // for now...
+
+ virtual __int64 songSizeOnHardDrive(songid_t song) {return getTrackSize(song);} // how big a song will be when copied back. Return -1 for not supported.
+
+ virtual int copyToHardDrive(songid_t song, // the song to copy
+ wchar_t * path, // path to copy to, in the form "c:\directory\song". The directory will already be created, you must append ".mp3" or whatever to this string! (there is space for at least 10 new characters).
+ void * callbackContext, //pass this to the callback
+ void (*callback)(void * callbackContext, wchar_t * status), // call this every so often so the GUI can be updated. Including when finished!
+ int * killswitch // if this gets set to anything other than zero, the transfer has been cancelled by the user
+ ); // -1 for failed/not supported. 0 for success.
+};
+
+#endif //_NJBDEVICE_H_ \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_njb/NOMAD DAP PC SDK v3_5.pdf b/Src/Plugins/Portable/pmp_njb/NOMAD DAP PC SDK v3_5.pdf
new file mode 100644
index 00000000..b044b758
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/NOMAD DAP PC SDK v3_5.pdf
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_njb/Nmsdk.h b/Src/Plugins/Portable/pmp_njb/Nmsdk.h
new file mode 100644
index 00000000..892e04bb
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/Nmsdk.h
@@ -0,0 +1,922 @@
+/////////////////////////////////////////////////////////////////////////////////////
+//
+// File: nmsdk.h
+//
+// Purpose: Combination of the Jukebox SDK2 header file and the NomadII SDK2 header file.
+//
+// Notes: Please make sure to include the COM Stuffs <Objbase.h> and <initguid.h>
+// at the beginning of the application programs. Otherwise, it will get Link Errors.
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+// PARTICULAR PURPOSE.
+//
+// Copyright (c) Creative Technology Ltd., 2001. All rights reserved.
+//
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+// nmsdk.h : main header file for export
+// Version: 1.0.7.0
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef __nmsdk_h__
+#define __nmsdk_h__
+
+//
+// COM Interface Declaration
+//
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+
+//////////////////////////////////////////////////////////////////////////
+// Class ID //
+//////////////////////////////////////////////////////////////////////////
+//CLSID CLSID_CTJukeBox2:
+// {BD1A6357-3E9B-4f1b-8375-AEE989ED6C5E}
+DEFINE_GUID(CLSID_CTJukeBox2,
+0xbd1a6357, 0x3e9b, 0x4f1b, 0x83, 0x75, 0xae, 0xe9, 0x89, 0xed, 0x6c, 0x5e);
+
+//CLSID CLSID_CTNOMAD2:
+// {0EBE3156-FD3A-4f5c-ABDB-71E3BEEAD091}
+DEFINE_GUID(CLSID_CTNOMAD2,
+0xebe3156, 0xfd3a, 0x4f5c, 0xab, 0xdb, 0x71, 0xe3, 0xbe, 0xea, 0xd0, 0x91);
+
+
+//////////////////////////////////////////////////////////////////////////
+// return codes //
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////
+// JukeBox & NomadII shared stuffs //
+//////////////////////////////////////////////////////
+// Additional Error codes
+#define DAPSDK_SUCCESS 0x00
+// General Error
+#define DAPSDK_FAILED 0x01
+#define DAPSDK_E_DEVICE_BUSY 0x02
+#define DAPSDK_E_STORAGE_FULL 0x03
+#define DAPSDK_E_SETTIME_REJECTED 0x05
+#define DAPSDK_E_ITEM_SIZE_MISSING 0x14 //NMJB_E_TRACK_SIZE_MISSING
+#define DAPSDK_E_ITEM_UPLOAD_DENIED 0x18 //NMJB_E_TRACK_UPLOAD_DENIED
+#define DAPSDK_E_PLAYER_NOT_CONNECTED 0x80
+#define DAPSDK_E_CANCELLED 0x81
+#define DAPSDK_E_PORT_UNAVAILABLE 0x82
+#define DAPSDK_E_OUT_OF_MEMORY 0x83
+#define DAPSDK_E_FILEOPEN_ERR 0x84
+#define DAPSDK_E_ITEM_NOT_FOUND 0x85
+#define DAPSDK_E_LOAD_COMPONENTS_FAILED 0x86
+#define DAPSDK_E_ID_INVALID 0x87
+#define DAPSDK_E_FILETYPE_ILLEGAL 0x88
+#define DAPSDK_E_LOADRES_FAIL 0x89
+#define DAPSDK_E_FORMAT_NOT_FOUND 0x8a
+#define DAPSDK_E_FILE_ALREADY_EXISTS 0x8b
+#define DAPSDK_E_LIB_CORRUPTED 0x8c
+#define DAPSDK_E_LIB_BUSY 0x8d
+#define DAPSDK_E_FILE_READ_WRITE_FAILED 0x8e //NMJB_E_FILE_WRITE_FAILED
+#define DAPSDK_E_INVALID_FILEPATH 0x8f
+#define DAPSDK_E_UNSUPPORTED 0x91
+#define DAPSDK_E_NORIGHTS 0x95
+#define DAPSDK_E_UNDEFINED_ERR 0xff
+
+
+//////////////////////////////////////////////////////
+// NomadII stuffs //
+//////////////////////////////////////////////////////
+#define DAPSDK_E_SMARTMEDIA_WRITE_PROTECTED 0x98
+#define DAPSDK_E_NO_STORAGE 0x99 //No internal media or smart media
+
+//////////////////////////////////////////////////////
+// JukeBox stuffs //
+//////////////////////////////////////////////////////
+// General Error
+#define DAPSDK_E_HD_GENERAL_ERROR 0x04
+// Track Management Error
+#define DAPSDK_E_TRACK_NOT_FOUND 0x10
+#define DAPSDK_E_TRACK_ALREADY_EXIST 0x11
+#define DAPSDK_E_TRACK_TITLE_MISSING 0x12
+#define DAPSDK_E_TRACK_CODEC_MISSING 0x13
+#define DAPSDK_E_TRACK_IO_OPERATION_ABORTED 0x15
+#define DAPSDK_E_TRACK_READ_WRITE_ERROR 0x16
+#define DAPSDK_E_TRACK_NOT_OPENED 0x17
+// Playlist Error
+#define DAPSDK_E_PL_NOT_FOUND 0x20
+#define DAPSDK_E_PL_ALREADY_EXIST 0x21
+#define DAPSDK_E_PL_ITEM_NOT_FOUND 0x22
+#define DAPSDK_E_PL_ITEM_ALREADY_EXIST 0x23
+//Additional
+#define DAPSDK_E_DISKFULL_FOR_DOWNLOAD 0x90
+
+
+#define DAPSDK_E_STATUS_TIMEOUT 0x06
+
+// Transport Control Error
+#define DAPSDK_E_END_OF_TRACK 0x30
+#define DAPSDK_E_END_OF_LIST 0x31
+#define DAPSDK_E_CODEC_NOT_SUPPORTED 0x32
+#define DAPSDK_E_DATA_CORRUPTED 0x33
+#define DAPSDK_E_SAMPLING_RATE_NOT_SUPPORTED 0x34
+#define DAPSDK_E_DECODING_ERROR 0x35
+#define DAPSDK_E_POSITION_OUTOF_RANGE 0x36
+#define DAPSDK_E_NOT_STOPPED 0x37
+
+// Audio Control Error
+#define DAPSDK_E_UNKNOW_PROPERTY 0x40
+#define DAPSDK_E_VALUE_OUTOF_RANGE 0x41
+
+// USB Transaction Error
+#define DAPSDK_E_DATA_FILE_NOT_FOUND 0x60
+#define DAPSDK_E_DATA_FILE_TOO_BIG 0x61
+#define DAPSDK_E_DATA_FILE_ALREADY_EXIST 0x62
+#define DAPSDK_E_TOO_MANY_DATA_FILES 0x63
+
+//additional
+#define DAPSDK_E_WMDM_INIT_FAILED 0x92
+#define DAPSDK_E_INVALID_ARGUMENT 0x93
+#define DAPSDK_E_PARENTNODE_NOT_EXIST 0x94
+#define DAPSDK_E_NORIGHTS 0x95
+#define DAPSDK_E_PATH_EXCESS_LEN 0x96
+#define DAPSDK_E_LOAD_PROC_FAILED 0x97
+
+// New Error code for MultiApplication assess of Nomad Jukebox 2/3/Zen
+#define DAPSDK_E_PMSMAN_CREATEDIRECTORY_FAILED 0x0100
+#define DAPSDK_E_DEVICE_WRITE_FAILED 0x0200
+#define DAPSDK_E_DEVICE_READ_FAILED 0x0300
+#define DAPSDK_E_DB_INVALID_REQUEST_ID 0x0400
+#define DAPSDK_E_DB_INVALID_NODE_ID 0x0500
+#define DAPSDK_E_DWNTHRD_CREATEMETADATA_FAILED 0x0600
+#define DAPSDK_E_DEVINFO_INVALID_INDEX 0x0700
+#define DAPSDK_E_INVALID_DEVICESETTINGTYPE 0x0800
+#define DAPSDK_E_FILESIZE_TOO_BIG 0x0900
+#define DAPSDK_E_AUDIOFILE_FORMAT 0x0A00
+#define DAPSDK_E_AUDIOFILE_INVALID 0x0B00
+#define DAPSDK_E_ACCESS_DENIED 0x0C00
+#define DAPSDK_E_FILE_NOT_FOUND 0x0D00
+#define DAPSDK_E_EOF 0x0E00
+#define DAPSDK_E_COOKIE 0x0F00
+#define DAPSDK_E_PLAYBACK_INPROGRESS 0x1000
+#define DAPSDK_E_TRANSFER_INPROGRESS 0x1100
+#define DAPSDK_E_BUFFER_NOT_ENOUGH 0x1200
+
+// New Error code for Data Folder of Nomad Jukebox 2/3/Zen
+#define DAPSDK_E_NOT_A_FOLDER 0x1400 // the target file is not a folder
+#define DAPSDK_E_FOLDER_NOT_EMPTY 0x1600 // the target folder is not empty
+#define DAPSDK_E_FOLDER_EXIST 0x1700 // the target folder exist
+#define DAPSDK_E_FOLDER_NOTEXIST 0x1800 // the target folder does not exist
+#define DAPSDK_E_PARENTFOLDER_NOTEXIST 0x1900 // the target parent folder does not exist
+#define DAPSDK_E_FILEPATH_TOOLONG 0x1A00 // the target file path is too long
+#define DAPSDK_E_FILENAME_TOOLONG 0x1B00 // the target file name is too long
+#define DAPSDK_E_INVALID_OPERATION 0x1E00 // the operation cannot be perform
+
+
+//////////////////////////////////////////////////////////////////////////
+// Definitions for WM Messages //
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////
+// JukeBox & NomadII shared stuffs //
+//////////////////////////////////////////////////////
+// Download message
+#define WM_DAPSDK_DOWNLOAD_PROGRESS WM_USER+500
+#define WM_DAPSDK_DOWNLOAD_COMPLETE WM_USER+501
+
+// Upload message
+#define WM_DAPSDK_GETITEM_PROGRESS WM_USER+502
+#define WM_DAPSDK_GETITEM_COMPLETE WM_USER+503
+
+//////////////////////////////////////////////////////
+// JukeBox stuffs //
+//////////////////////////////////////////////////////
+// Device change message
+#define WM_DAPSDK_JUKEBOX_REMOVAL WM_USER+508
+#define WM_DAPSDK_JUKEBOX_ARRIVAL WM_USER+509
+// Playback message
+#define WM_DAPSDK_PLAYBACK_COMPLETE WM_USER+504
+#define WM_DAPSDK_PLAYLIST_COMPLETE WM_USER+505
+#define WM_DAPSDK_PLAYBACK_ERROR WM_USER+506
+#define WM_DAPSDK_PLAYBACK_PROGRESS WM_USER+507
+
+
+// New callback messages for MultiApplication assess of Hotcake
+// Notification Messages for database change
+#define WM_DAPSDK_CHANGE_BASE WM_USER + 200
+#define WM_DAPSDK_MUSIC_ADD_FILE WM_DAPSDK_CHANGE_BASE + 0 // wParam = NodeId added, lParam = nil
+#define WM_DAPSDK_MUSIC_DEL_FILE WM_DAPSDK_CHANGE_BASE + 1 // wParam = NodeId deleted, lParam = nil
+#define WM_DAPSDK_MUSIC_SETATTR_FILE WM_DAPSDK_CHANGE_BASE + 2 // wParam = NodeId edited, lParam = nil
+#define WM_DAPSDK_DATA_ADD_FILE WM_DAPSDK_CHANGE_BASE + 3 // wParam = NodeId added, lParam = nil
+#define WM_DAPSDK_DATA_DEL_FILE WM_DAPSDK_CHANGE_BASE + 4 // wParam = NodeId deleted, lParam = nil
+#define WM_DAPSDK_DATA_SETATTR_FILE WM_DAPSDK_CHANGE_BASE + 5 // wParam = NodeId edited, lParam = nil
+#define WM_DAPSDK_PLAYLIST_ADD_FILE WM_DAPSDK_CHANGE_BASE + 6 // wParam = NodeId added, lParam = nil
+#define WM_DAPSDK_PLAYLIST_DEL_FILE WM_DAPSDK_CHANGE_BASE + 7 // wParam = NodeId deleted, lParam = nil
+#define WM_DAPSDK_PLAYLIST_SETATTR_FILE WM_DAPSDK_CHANGE_BASE + 8 // wParam = NodeId edited, lParam = nil
+#define WM_DAPSDK_PLAYLIST_ITEM_CHANGE WM_DAPSDK_CHANGE_BASE + 9 // wParam = PlaylistNodeId affected, lParam = nil
+#define WM_DAPSDK_STORAGEINFO_CHANGE WM_DAPSDK_CHANGE_BASE + 10 // wParam = deviceIndex that change occurred, lParam = nil
+
+
+#define WM_DAPSDK_ADDITEM_PROGRESS WM_USER+500
+#define WM_DAPSDK_ADDITEM_COMPLETE WM_USER+501
+
+
+/////////////////////////////////////////////////////////////////////////
+// struct defines //
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////
+// JukeBox & NomadII shared stuffs //
+//////////////////////////////////////////////////////
+typedef struct _DAPSDK_DATE_TIME
+{
+ short Year, Month, Day, DayOfWeek;
+ short Hour, Min, Sec, MilliSec;
+} DATE_TIME, *PDATE_TIME;
+
+typedef struct _DAPSDK_FORMAT_INFO
+{
+ long lCodecID;
+ UCHAR szCodecName[128];
+ long lSamplingRate;
+ long lNumChannel;
+} FORMAT_INFO, *PFORMAT_INFO;
+
+//-- SDK and firmware version info structure --//
+//
+// used in GetSDKVersion()
+// used in GetDeviceProperties( kFirmwareVersion, kHardwareVersion)
+typedef struct _DAPSDK_VERSION
+{
+ WORD major;
+ WORD minor;
+ WORD build;
+ WORD specialBuild;
+} DAPSDK_VERSION, *PDAPSDK_VERSION;
+
+//-- Memory Storage Information Structure --//
+typedef struct _DAPSDK_STORAGE_INFO
+{
+ ULONG totalH;
+ ULONG totalL;
+ ULONG freeH;
+ ULONG freeL;
+} DAPSDK_STORAGE_INFO, *PDAPSDK_STORAGE_INFO;
+
+//-- Identification structure --//
+// same for RootItem, ParentItem and Item now //
+typedef struct _DAPSDK_ID
+{
+ long lID; // stores the unique ID
+ long lType; // stores the type (see enum above)
+ BSTR bstrName; // stores the name
+} DAPSDK_ID, *PDAPSDK_ID;
+
+//////////////////////////////////////////////////////
+// NomadII stuffs //
+//////////////////////////////////////////////////////
+typedef struct _DAPSDK_RADIOPRESET
+{
+ DWORD dwPresetIndex;
+ DWORD dwPresetValue;
+} RADIOPRESET, *PRADIOPRESET;
+
+
+// device info struct
+typedef struct _DAPSDK_DEVICE_INFO
+{
+ BYTE cDeviceId[16]; // player's ID
+ long dwFirmwareVersion; // player's firmware version
+ long dwHardwareVersion; // player's hardware version
+ char cDeviceName[32]; // player's name w/o NULL terminator
+ BYTE byPowerSource; // player's power source status in percent
+} DAPSDK_DEVICE_INFO, *PDAPSDK_DEVICE_INFO;
+
+enum
+{
+ kASCII = 0,
+ kBINARY,
+ kUNICODE,
+};
+
+//////////////////////////////////////////////////////
+// Item type for Jukebox (used by lRootItemType, //
+// lParentItemType, lItemType parameter) //
+//////////////////////////////////////////////////////
+enum
+{
+ kAudioTrackType = 1, // this is a audio track
+ kPlaylistType, // this is a track in the playlist
+ kDataFileType, // this is a data file
+ kDataFolderType, // this is a data file folder
+};
+
+/////////////////////////////////////////////////////////
+// Item type for NomadII (used in lItemType parameter) //
+/////////////////////////////////////////////////////////
+enum
+{
+ kInternalMemType = 0, // this item resides in internal memory
+ kExternalMemType, // this item resides in external (removeable) media
+};
+
+
+// Device Property Type Constant
+//-- Device Property type (used by lDevicePropertyType parameter in --//
+// GetDeviceProperties/SetDeviceProperties --//
+enum
+{
+//////////////////////////////////////////////////////
+// JukeBox & NomadII shared stuffs //
+//////////////////////////////////////////////////////
+ kDeviceSerialNumberValue = 1, // GET, <NOMAD II>given the Item type
+ // in "lpValue" parameter, the return value
+ // is a pointer to the serial number
+ // of the specified media.
+ kFirmwareVersion, // GET, return value is a pointer to
+ // DAPSDK_VERSION structure indicating the
+ // current firmware version in the device.
+ kDeviceNameString, // GET, return value is a pointer to
+ // BSTR string.
+ kPowerSourceValue,
+ kStorageInfoStruct, // GET, return value is a pointer to
+ // DAPSDK_STORAGE_INFO structure indicating
+ // the current memory usage.
+ kDateTimeStruct, // GET/SET, return/set value is a pointer to
+ // DATE_TIME structure (defined in SDK 1.0)
+ kOwnerNameString, // GET/SET, return/set value is a pointer to BSTR
+ // string.
+ kAudioFormatCount, // GET, return value is a pointer to the number of
+ // audio format supported by device.
+ kAudioFormatInfoStruct, // GET, given the index value in "lpValue"
+ // parameter, the return value is a pointer to
+ // FORMATINFO structure (defined in SDK 1.0).
+ kLangEncodeSupport, // GET, returns unsigned long value
+ // 0x01 == Latin 1(CP1252), 0x80000000 == UNICODE
+
+//////////////////////////////////////////////////////
+// JukeBox stuffs //
+//////////////////////////////////////////////////////
+ kHardwareVersion, // GET, return value is a pointer to
+ // DAPSDK_VERSION structure indicating the
+ // current hardware version in the device.
+ kAudioVolumeValue, // GET/SET, return/set value is the pointer to the
+ // volume level in percentage.
+ kAudioMuteValue, // GET/SET, return/set value is the pointer to the
+ // Mute status (1=on, 0=off).
+ // Treble level in percentage.
+ kEAXCount, // GET, return value is the pointer to the number
+ // of EAX settings.
+ kEAXNameString, // GET, given the index value in "lpValue"
+ // parameter, the return value is a pointer to
+ // a BSTR string.
+ kEAXAmountValue, // GET/SET, given the index value in "lpValue"
+ // parameter, the return value is the effect
+ // amount in percentage.
+ kEAXCurrentIndex, // GET/SET, the value in "lpValue" is used to set
+ // or retrieve the current EAX selection.
+ kAudioEQPresetCount, // GET, return value is the pointer to the number
+ // of EQ settings.
+ kAudioEQNameString, // GET, given the index value in "lpValue"
+ // parameter, the return value is a pointer to
+ // a BSTR string.
+ kAudioEQAmountValue, // GET/SET, return/set value is the pointer to the
+ // EQ amount in percentage.
+ kAudioEQCurrentIndex, // GET/SET, the value in "lpValue" is used to set
+ // or retrieve the current EQ selection.
+
+//////////////////////////////////////////////////////
+// NomadII stuffs //
+//////////////////////////////////////////////////////
+ kFMRadioPresetCount, // GET, returns pointer to number of FM radio
+ // presets available in the player.
+ kFMRadioPresetValue, // GET/SET, given the preset index value,
+ // returns an existing value or sets a new
+ // preset FM preset. Value in kHz.
+ kFormatStorage // SET, Do format storage.
+};
+//Note that all index value is zero-based. Client should call the "kxxxxxxCount" property first before trying to get the value for individual settings in list-type of properties, i.e. kEAXNameString, kAudioFormatInfoStruct...
+//Not all properties are readable and writable, some are read-only attributes. Those properties that are read-only are marked with the "GET" strings and those that are read and writable are marked as "GET/SET". Client should not call SetDeviceProperties() with the read-only property type, such call would fail.
+//New Property types maybe supported in future by simply adding into the enum list, and publish to developers.
+
+
+//////////////////////////////////////////////////////
+// JukeBox stuffs //
+//////////////////////////////////////////////////////
+//-- Playback operation type (used by lPlayOperationType parameter in --//
+//-- PlayControl() & QueueControl()) --//
+enum
+{
+ kPlayTrack = 1, // lpValue stores the pointer to the
+ // DAPSDK_ITEM_ID structure.
+ kStopTrack, // lpValue is not used, stop current track, no op
+ // if no track is currently playing.
+ kPauseTrack, // lpValue is not used, pause current track, no op
+ // if no track is currently playing.
+ kSetPlaybackPosition, // lpValue stores the pointer to the new playback
+ // position.
+ kQueueTrack, // lpValue stores the pointer to the
+ // DAPSDK_ITEM_ID structure.
+ kClearQueue, // lpValue is not used, clear existing queue.
+};
+
+#define TITLE "TITLE"
+#define FILESIZE "FILE SIZE"
+#define CODEC "CODEC"
+#define ALBUM "ALBUM"
+#define ARTIST "ARTIST"
+#define GENRE "GENRE"
+#define LENGTH "LENGTH"
+#define TRACKNUM "TRACK NUM"
+#define YEAR "YEAR"
+#define PLAYONLY "PLAYONLY"
+#define TRACKID "TRACK ID"
+
+// new attribute for datafile and datafolder
+#define MOD_FILETIME "MODIFIED FILETIME"
+#define FILE_ATTRIB "FILE ATTRIB"
+#define PARENT_FOLDER "PARENT FOLDER"
+#define FOLDERNAME "FOLDER NAME" // data folder name for Nomad Jukebox
+#define MULTI_ATTRIB "MULTI ATTRIB" // this is for user to change multiple file attribute at one time.
+
+#define FILENAME "FILE NAME" // file name for Nomad II, data file name for Nomad Jukebox
+
+#define ALLTRACKSKEY -1
+
+//////////////////////////////////////////////////////
+// NomadII stuffs //
+//////////////////////////////////////////////////////
+#define DOS_FILEATTRIB "DOS_FILEATTRIB"
+#define DOS_DATETIME "DOS_DATETIME"
+
+
+//////////////////////////////////////////////////////////////////////////
+// ICTJukebox (Interface 1) Methods //
+//////////////////////////////////////////////////////////////////////////
+
+// {DFC9207F-4B64-11D4-A4ED-00A0C98E46CC}
+DEFINE_GUID(IID_ICTJukebox,
+0xdfc9207f, 0x4b64, 0x11d4, 0xa4, 0xed, 0x00, 0xa0, 0xc9, 0x8e, 0x46, 0xcc);
+
+interface ICTJukebox : public IUnknown
+{
+ virtual HRESULT STDMETHODCALLTYPE CancelTransfer(
+ /*[in]*/long lDeviceID) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RenamePlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lPlaylistID,
+ /*[in]*/BSTR bstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RemoveTracksFromPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lTrackCount,
+ /*[in]*/long* lpTrackList,
+ /*[in]*/long lPlaylist) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE AddTracksToPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lTrackCount,
+ /*[in]*/long* lpTrackList,
+ /*[in]*/long lPlaylist) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE InsertPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/BSTR bstrPlaylistName,
+ /*[out]*/long* lpPlaylistID) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE DeletePlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lPlaylistID) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindNextTrackInPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lPlaylistID,
+ /*[out]*/long* lpTrackID,
+ /*[out]*/BSTR* lpbstrTrackName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstTrackInPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lPlaylistID,
+ /*[out]*/long* lpTrackID,
+ /*[out]*/BSTR* lpbstrTrackName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindNextPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[out]*/long* lpPlaylistID,
+ /*[out]*/BSTR* lpbstrPlaylistName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstPlaylist(
+ /*[in]*/long lDeviceID,
+ /*[out]*/long* lpPlaylistID,
+ /*[out]*/BSTR* lpbstrPlaylistName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ChangeTrackInfo(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lTarckID,
+ /*[in]*/long lSize,
+ /*[in]*/IUnknown* lpTrackInfo) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE DeleteTrack(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lTrackID) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE InsertTrack(
+ /*[in]*/long lDeviceID,
+ /*[in]*/BSTR bstrFilePath,
+ /*[in]*/long lSize,
+ /*[in]*/IUnknown* lpTrackInfo,
+ /*[out]*/long* lpTrackID) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetTrackInfo(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lTrackID,
+ /*[in]*/long lInSize,
+ /*[out]*/long* lpOutSize,
+ /*[out]*/IUnknown* lpTrackInfo) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindNextTrack(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lKeyID,
+ /*[out]*/long* lpTrackID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstTrack(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lKeyID,
+ /*[out]*/long* lpTrackID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindNextKey(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lRootKeyID,
+ /*[out]*/long* lpKeyID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstKey(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long lRootKeyID,
+ /*[out]*/long* lpKeyID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindNextRootKey(
+ /*[in]*/long lDeviceID,
+ /*[out]*/long* lpRootKeyID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstRootKey(
+ /*[in]*/long lDeviceID,
+ /*[out]*/long* lpRootKeyID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE SetOwnerName(
+ /*[in]*/long lDeviceID,
+ /*[in]*/BSTR bstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetOwnerName(
+ /*[in]*/long lDeviceID,
+ /*[out]*/BSTR* lpbstrName) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE SetDateTime(
+ /*[in]*/long lDeviceID,
+ /*[out]*/IUnknown* lpDateTime) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetDateTime(
+ /*[in]*/long lDeviceID,
+ /*[out]*/IUnknown* lpDateTime) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindNextFormatSupport(
+ /*[in]*/long lDeviceID,
+ /*[out]*/IUnknown* lpFormatInfo) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstFormatSupport(
+ /*[in]*/long lDeviceID,
+ /*[out]*/IUnknown* lpFormatInfo) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetStorageInfo(
+ /*[in]*/long lDeviceID,
+ /*[out]*/unsigned long* lpTotalMemHigh,
+ /*[out]*/unsigned long* lpTotalMemLow,
+ /*[out]*/unsigned long* lpFreeMemHigh,
+ /*[out]*/unsigned long* lpFreeMemLow ) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceInfo(
+ /*[in]*/long lDeviceID,
+ /*[out]*/IUnknown* lpDeviceInfo) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceCount(
+ /*[out]*/long* lpDeviceCount) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE SetCallbackWindow(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long hWnd) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetSDKVersion(
+ /*[out]*/long* lpVersion) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ShutDown() = 0;
+ virtual HRESULT STDMETHODCALLTYPE Initialize() = 0;
+
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+// ICTJukebox2 (Interface 2) Methods //
+//////////////////////////////////////////////////////////////////////////
+
+DEFINE_GUID(IID_ICTJukebox2,
+0xdfc92080, 0x4b64, 0x11d4, 0xa4, 0xed, 0x00, 0xa0, 0xc9, 0x8e, 0x46, 0xcc);
+
+interface ICTJukebox2 : public IUnknown
+{
+ virtual HRESULT STDMETHODCALLTYPE Initialize2() = 0;
+ virtual HRESULT STDMETHODCALLTYPE ShutDown2() = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetCallbackWindow2(
+ long lDeviceID,
+ long hWnd ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceCount2(
+ long* lpDeviceCount ) = 0;
+
+ //-- Canceling I/O --//
+ virtual HRESULT STDMETHODCALLTYPE CancelTransfer2(
+ long lDeviceID ) = 0;
+
+
+ //-- GetSDKVersion2() Fucntion Descriptions --//
+ // This function overides the same function in interface 1, this is to
+ // return a more meaningful version information to the client which is
+ // clearly specified in the DAPSDK_VERSION structure
+ virtual HRESULT STDMETHODCALLTYPE GetSDKVersion2(
+ IUnknown* lpSDKVersion ) = 0;
+
+
+ //_______________________ Querying RootItem __________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstRootItem (
+ long lDeviceID,
+ IUnknown* lpRootItemID ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE FindNextRootItem (
+ long lDeviceID,
+ IUnknown* lpRootItemID ) = 0;
+
+ //______________________ Querying ParentItem __________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstParentItem (
+ long lDeviceID,
+ IUnknown* lRootItemID,
+ IUnknown* lpParentItemID ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE FindNextParentItem (
+ long lDeviceID,
+ IUnknown* lRootItemID,
+ IUnknown* lpParentItemID ) = 0;
+
+
+ //______________________ ParentItem Management __________________________ //
+
+ //-- AddParentItem() Fucntion Descriptions --//
+ // Client can only add Parent Item of the same type as the RootItem. For
+ // example, client cannot add a kDataFileType Parent item into a RootItem
+ // type of kAudioTrackType.
+ //
+ // As of current firmware implementation, client can only add ParentItem
+ // into a RootItem that has a type of kPlaylistType. Adding ParentItem into
+ // other RootItem type will fail.
+ virtual HRESULT STDMETHODCALLTYPE AddParentItem (
+ long lDeviceID,
+ IUnknown* lRootItemID,
+ IUnknown* lParentItemID ) = 0;
+
+ //-- DeleteParentItem() Function Description --//
+ // Client can only delete ParentItem of type kPlaylistType. Deleting other
+ // ParentItem type will fail.
+ virtual HRESULT STDMETHODCALLTYPE DeleteParentItem (
+ long lDeviceID,
+ IUnknown* lParentItemID ) = 0;
+
+ //-- RenameParentItem() Function Description --//
+ // Client can only rename ParentItem of type kPlaylistType. Renaming other
+ // ParentItem type will fail.
+ //
+ // The updated DAPSDK_PARENTITEM_ID structure is returned in lParentItemID
+ // parameter.
+ virtual HRESULT STDMETHODCALLTYPE RenameParentItem (
+ long lDeviceID,
+ IUnknown* lParentItemID,
+ BSTR bstrNewParentItemName ) = 0;
+
+
+ //__________________________ Querying Item ______________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstItem (
+ long lDeviceID,
+ IUnknown* lParentItemID,
+ IUnknown* lpItemID ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE FindNextItem (
+ long lDeviceID,
+ IUnknown* lParentItemID,
+ IUnknown* lpItemID ) = 0;
+
+
+ //_____________________ Getting Item Attributes __________________________//
+
+ //-- GetItemAttribute() Function Description --//
+ // This function returns the TrackInfo data for all Item type. But for
+ // kDataFileType and kPlaylistFileType, the TrackInfo data contain only
+ // file name, file type and file size. In contrast, the item attribute
+ // for a kAudioTrackType may contain all information similar to the ID3tag
+ // information that a typical MP3 file has.
+ virtual HRESULT STDMETHODCALLTYPE GetItemAttribute (
+ long lDeviceID,
+ IUnknown* lpItemID,
+ long lInItemInfoSize,
+ long* lOutItemInfoSize,
+ IUnknown* lpItemInfo ) = 0;
+
+
+ //-- SetItemAttribute() Function Description --//
+ // This function allows client to set TrackInfo attributes of a
+ // kAudioTrackItem only. Setting other item type will fail.
+ //
+ // It allows client to set a particular attribute data according to the name
+ // and type given in the parameters. Note that only one attribute can be set
+ // at a time.
+ virtual HRESULT STDMETHODCALLTYPE SetItemAttribute (
+ long lDeviceID,
+ IUnknown* lpItemID,
+ BSTR bstrAttributeName,
+ long lAttributeType,
+ long lAttributeDataSize,
+ IUnknown* lpAttributeData ) = 0;
+
+
+ //____________________________ Item Management ___________________________//
+
+ //-- AddItem() Function Description --//
+ // This function initiates the file download from the computer to the
+ // device. Client should call this function to download audio tracks
+ // like MP3, WMA and WAVE and data files. Client are not allow
+ // to download an item of kPlaylistType, use AddItemsToParentItem to add
+ // item of type kPlaylistType.
+ virtual HRESULT STDMETHODCALLTYPE AddItem (
+ long lDeviceID,
+ long lItemType,
+ BSTR bstrSrcFileName,
+ long lItemInfoSize,
+ IUnknown* lpItemInfo) = 0;
+
+ //-- AddItemsToParentItem() Function Description --//
+ // Client call this function to add kAudioTrackType item into the ParentItem
+ // of type kPlaylistType. Only kAudioTrackType items are accepted, and only
+ // ParentItem of type kPlaylistType can accept such addition.
+ virtual HRESULT STDMETHODCALLTYPE AddItemsToParentItem(
+ long lDeviceID,
+ IUnknown* lpParentItemID,
+ long lItemIDCount,
+ IUnknown* lpItemIDList ) = 0;
+
+ //-- DeleteItem() Function Description --//
+ // Client can call this function to remove item of type kAudioTrackType and
+ // kDataFileType. You cannot remove kPlaylistType item here.
+ virtual HRESULT STDMETHODCALLTYPE DeleteItem (
+ long lDeviceID,
+ IUnknown* lpItemID ) = 0;
+
+ //-- GetItem() Function Description --//
+ // Client can call this function to retrieve item that are kAudioTrackType
+ // and kDataFileType from the device to the computer. Note that client
+ // cannot retrieve item of type kPlaylistItem
+ virtual HRESULT STDMETHODCALLTYPE GetItem (
+ long lDeviceID,
+ BSTR bstrDestinationFileName,
+ IUnknown* lpItemID ) = 0;
+
+
+ //________________________ Device Properties ____________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceProperties (
+ long lDeviceID,
+ long lDevicePropertyType,
+ IUnknown* lpValue ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetDeviceProperties (
+ long lDeviceID,
+ long lDevicePropertyType,
+ IUnknown* lpValue ) = 0;
+
+
+ //_______________________ Playback control ______________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE PlayControl (
+ long lDeviceID,
+ long lPlayOperationType,
+ IUnknown* lpValue ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE QueueControl (
+ long lDeviceID,
+ long lQueueOperationType,
+ IUnknown* lpValue ) = 0;
+
+};
+
+//////////////////////////////////////////////////////////////
+// ICTNomad2 Methods //
+//////////////////////////////////////////////////////////////
+
+// {368953D4-6A2F-4787-BC6F-4047A39A7557}
+DEFINE_GUID(IID_ICTNomad2,
+0x368953d4, 0x6a2f, 0x4787, 0xbc, 0x6f, 0x40, 0x47, 0xa3, 0x9a, 0x75, 0x57);
+
+interface ICTNomad2 : public IUnknown
+{
+ virtual HRESULT STDMETHODCALLTYPE Initialize() = 0;
+ virtual HRESULT STDMETHODCALLTYPE ShutDown() = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE SetCallbackWindow(
+ /*[in]*/long lDeviceID,
+ /*[in]*/long hWnd) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceCount(
+ /*[out]*/long* lpDeviceCount) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE CancelTransfer(
+ /*[in]*/long lDeviceID) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetSDKVersion(
+ /*[out]*/IUnknown* lpVersion) = 0;
+
+
+ //__________________________ Querying Item ______________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE FindFirstItem (
+ long lDeviceID,
+ IUnknown* lParentItemID,
+ IUnknown* lpItemID ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE FindNextItem (
+ long lDeviceID,
+ IUnknown* lParentItemID,
+ IUnknown* lpItemID ) = 0;
+
+
+ //_____________________ Getting Item Attributes __________________________//
+
+ //-- GetItemAttribute() Function Description --//
+ // This function returns the TrackInfo data for all Item type. But for
+ // kDataFileType and kPlaylistFileType, the TrackInfo data contain only
+ // file name, file type and file size. In contrast, the item attribute
+ // for a kAudioTrackType may contain all information similar to the ID3tag
+ // information that a typical MP3 file has.
+ virtual HRESULT STDMETHODCALLTYPE GetItemAttribute (
+ long lDeviceID,
+ IUnknown* lpItemID,
+ long lInItemInfoSize,
+ long* lOutItemInfoSize,
+ IUnknown* lpItemInfo ) = 0;
+
+
+ //____________________________ Item Management ___________________________//
+
+ //-- AddItem() Function Description --//
+ // This function initiates the file download from the computer to the
+ // device. Client should call this function to download audio tracks
+ // like MP3, WMA and WAVE and data files. Client are not allow
+ // to download an item of kPlaylistType, use AddItemsToParentItem to add
+ // item of type kPlaylistType.
+ virtual HRESULT STDMETHODCALLTYPE AddItem (
+ long lDeviceID,
+ long lItemType,
+ BSTR bstrSrcFileName,
+ long lItemInfoSize,
+ IUnknown* lpItemInfo) = 0;
+
+ //-- DeleteItem() Function Description --//
+ // Client can call this function to remove item of type kAudioTrackType and
+ // kDataFileType. You cannot remove kPlaylistType item here.
+ virtual HRESULT STDMETHODCALLTYPE DeleteItem (
+ long lDeviceID,
+ IUnknown* lpItemID ) = 0;
+
+ //-- GetItem() Function Description --//
+ // Client can call this function to retrieve item that are kAudioTrackType
+ // and kDataFileType from the device to the computer. Note that client
+ // cannot retrieve item of type kPlaylistItem
+ virtual HRESULT STDMETHODCALLTYPE GetItem (
+ long lDeviceID,
+ BSTR bstrDestinationFileName,
+ IUnknown* lpItemID ) = 0;
+
+
+ //________________________ Device Properties ____________________________//
+
+ virtual HRESULT STDMETHODCALLTYPE GetDeviceProperties (
+ long lDeviceID,
+ long lDevicePropertyType,
+ IUnknown* lpValue ) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetDeviceProperties (
+ long lDeviceID,
+ long lDevicePropertyType,
+ IUnknown* lpValue ) = 0;
+
+};
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif
+
diff --git a/Src/Plugins/Portable/pmp_njb/ctnmjb2.dll b/Src/Plugins/Portable/pmp_njb/ctnmjb2.dll
new file mode 100644
index 00000000..1d7d458a
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/ctnmjb2.dll
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_njb/main.cpp b/Src/Plugins/Portable/pmp_njb/main.cpp
new file mode 100644
index 00000000..53998610
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/main.cpp
@@ -0,0 +1,181 @@
+//#define PLUGIN_NAME "Nullsoft Creative NJB Plug-in"
+#define PLUGIN_VERSION L"0.57"
+
+#include "NJBDevice.h"
+#include "../Winamp/wa_ipc.h"
+
+#define WM_PMP_NJB_DEVICE_CONNECTED (WM_USER+23)
+int init();
+void quit();
+intptr_t MessageProc(int msg, intptr_t param1, intptr_t param2, intptr_t param3);
+
+PMPDevicePlugin plugin = {PMPHDR_VER, 0, init, quit, MessageProc};
+
+LRESULT CALLBACK CallbackWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+C_ItemList devices;
+
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+ICTJukebox2 * m_pCTJukebox2 = NULL;
+HWND mainMessageWindow = 0;
+static bool classRegistered = 0;
+HWND CreateDummyWindow()
+{
+ if (!classRegistered)
+ {
+ WNDCLASSW wc = {0, };
+
+ wc.style = 0;
+ wc.lpfnWndProc = CallbackWndProc;
+ wc.hInstance = plugin.hDllInstance;
+ wc.hIcon = 0;
+ wc.hCursor = NULL;
+ wc.lpszClassName = L"pmp_njb_window";
+
+ if (!RegisterClassW(&wc))
+ return 0;
+
+ classRegistered = true;
+ }
+ HWND dummy = CreateWindowW(L"pmp_njb_window", L"pmp_njb_window", 0, 0, 0, 0, 0, NULL, NULL, plugin.hDllInstance, NULL);
+
+ return dummy;
+}
+
+int init()
+{
+ waServiceFactory *sf = plugin.service->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,PmpNJBLangGUID);
+
+ static wchar_t szDescription[256];
+ swprintf(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_CREATIVE_NJB), PLUGIN_VERSION);
+ plugin.description = szDescription;
+
+ OleInitialize(NULL);
+ HRESULT hr = CoCreateInstance(CLSID_CTJukeBox2, NULL, CLSCTX_ALL, IID_ICTJukebox2, (void**) & m_pCTJukebox2);
+ if (hr != S_OK) return 0;
+
+ hr = m_pCTJukebox2->Initialize2();
+ if (hr != S_OK)
+ {
+ m_pCTJukebox2->Release();
+ m_pCTJukebox2=0;
+ return 0;
+ }
+
+ mainMessageWindow = CreateDummyWindow();
+ m_pCTJukebox2->SetCallbackWindow2(0, (long)mainMessageWindow);
+
+ long devCount = 0;
+ m_pCTJukebox2->GetDeviceCount2(&devCount);
+ for (long i = 1; i <= devCount; i++)
+ PostMessage(mainMessageWindow, WM_PMP_NJB_DEVICE_CONNECTED, i, 0);
+
+ return 0;
+}
+
+void quit()
+{
+
+ if (m_pCTJukebox2)
+ {
+ m_pCTJukebox2->ShutDown2();
+ m_pCTJukebox2->Release();
+ m_pCTJukebox2 = 0;
+ }
+ DestroyWindow(mainMessageWindow);
+ OleUninitialize();
+}
+
+LRESULT CALLBACK CallbackWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_PMP_NJB_DEVICE_CONNECTED:
+ {
+ new NJBDevice((int)wParam);
+ }
+ break;
+ case WM_DAPSDK_JUKEBOX_REMOVAL:
+ {
+ long devCount = 0;
+ if (m_pCTJukebox2->GetDeviceCount2(&devCount) == S_OK)
+ {
+ if (devCount == 0) // TODO benski> shouldn't we always register for messages?
+ m_pCTJukebox2->SetCallbackWindow2(0, (long)mainMessageWindow);
+ for (long i = 0; i < devices.GetSize(); /*i++*/)
+ {
+ NJBDevice * dev = (NJBDevice *)devices.Get(i);
+ bool attached = false;
+ for (long j = 1; j <= devCount; j++)
+ {
+ BYTE * ptr = NULL;
+ if (m_pCTJukebox2->GetDeviceProperties(j, kDeviceSerialNumberValue, (IUnknown*)ptr) == S_OK)
+ {
+ if (memcmp(ptr, dev->serial, 16) == 0)
+ {
+ attached = true;
+ dev->id = j;
+ break;
+ }
+ //free(ptr);
+ }
+ }
+ if (!attached)
+ {
+ SendMessage(plugin.hwndPortablesParent,WM_PMP_IPC,(intptr_t)dev,PMP_IPC_DEVICEDISCONNECTED);
+ delete dev;
+ }
+ else
+ i++;
+ }
+ }
+ }
+ break;
+ case WM_DAPSDK_JUKEBOX_ARRIVAL:
+ {
+ long devCount = 0;
+ if (m_pCTJukebox2->GetDeviceCount2(&devCount) == S_OK)
+ new NJBDevice(devCount);
+ }
+ break;
+ default:
+ {
+ for (int i = 0; i < devices.GetSize(); i++)
+ {
+ NJBDevice * dev = (NJBDevice *)devices.Get(i);
+ if (dev->messageWindow == hwnd)
+ return dev->WindowMessage(hwnd, uMsg, wParam, lParam);
+ }
+ }
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);;
+}
+
+intptr_t MessageProc(int msg, intptr_t param1, intptr_t param2, intptr_t param3) {
+ switch(msg) {
+ case PMP_DEVICECHANGE:
+ return 0;
+ case PMP_NO_CONFIG:
+ return TRUE;
+ case PMP_CONFIG:
+ return 0;
+ }
+ return 0;
+}
+
+extern "C" {
+ __declspec( dllexport ) PMPDevicePlugin * winampGetPMPDevicePlugin(){return &plugin;}
+ __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
+ int i = devices.GetSize();
+ while(i-- > 0) ((Device*)devices.Get(i))->Close();
+ return PMP_PLUGIN_UNINSTALL_NOW;
+ }
+}; \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_njb/pmp_njb.rc b/Src/Plugins/Portable/pmp_njb/pmp_njb.rc
new file mode 100644
index 00000000..47812072
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/pmp_njb.rc
@@ -0,0 +1,109 @@
+// 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
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDD_INVISI$(DISABLED) DIALOGEX 0, 0, 5, 5
+#else
+IDD_INVISI DIALOGEX 0, 0, 5, 5
+#endif
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+END
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RCDATA
+//
+
+IDR_ZEN_ICON RCDATA ".\\resources\\zenIcon.png"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_CREATIVE_NJB "Nullsoft Creative NJB Plug-in v%s"
+ 65535 "{4F5B2300-19D1-4390-BE04-89019441100B}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NJB_LOADING "NJB Loading..."
+ IDS_TRANSFERRING_PERCENT "Transferring %d%%"
+ IDS_DONE "Done"
+ IDS_ERROR "Error"
+ IDS_TRANSFERRING "Transferring..."
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Portable/pmp_njb/pmp_njb.sln b/Src/Plugins/Portable/pmp_njb/pmp_njb.sln
new file mode 100644
index 00000000..baa96ce9
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/pmp_njb.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29709.97
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pmp_njb", "pmp_njb.vcxproj", "{0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}"
+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
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Debug|Win32.Build.0 = Debug|Win32
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Debug|x64.ActiveCfg = Debug|x64
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Debug|x64.Build.0 = Debug|x64
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Release|Win32.ActiveCfg = Release|Win32
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Release|Win32.Build.0 = Release|Win32
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Release|x64.ActiveCfg = Release|x64
+ {0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {651853BB-3138-4841-BD5B-ACF5165F9161}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj b/Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj
new file mode 100644
index 00000000..7a12022e
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj
@@ -0,0 +1,315 @@
+<?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>{0D2DCCBD-FF58-4A04-BC53-D6208D5EB3E4}</ProjectGuid>
+ <RootNamespace>pmp_njb</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>
+ <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;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_ex_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x040c</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapExports>false</MapExports>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\replicant;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_ex_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;4311;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x040c</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapExports>false</MapExports>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\replicant;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_ex_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x040c</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\replicant;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_ex_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;4311;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x040c</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\General\gen_ml\itemlist.cpp" />
+ <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="NJBDevice.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\General\gen_ml\itemlist.h" />
+ <ClInclude Include="..\..\General\gen_ml\ml.h" />
+ <ClInclude Include="..\..\Library\ml_pmp\pmp.h" />
+ <ClInclude Include="NJBDevice.h" />
+ <ClInclude Include="Nmsdk.h" />
+ <ClInclude Include="resource.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="pmp_njb.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="resources\zenIcon.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj.filters b/Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj.filters
new file mode 100644
index 00000000..1eb41b59
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj.filters
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NJBDevice.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\itemlist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\ml_lib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="NJBDevice.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Nmsdk.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\General\gen_ml\itemlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\General\gen_ml\ml.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Library\ml_pmp\pmp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{19f63d8f-2d94-424b-b8e5-1ae189920736}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{675c67be-1ca6-4f01-aeb3-6b9329201724}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{df88e072-b108-4287-9243-92c4883b420e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{a2c922b0-d514-4e40-8e03-932a77c3e2fa}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="resources\zenIcon.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="pmp_njb.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Portable/pmp_njb/resource.h b/Src/Plugins/Portable/pmp_njb/resource.h
new file mode 100644
index 00000000..6f76d97a
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/resource.h
@@ -0,0 +1,23 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by pmp_njb1.rc
+//
+#define IDS_NJB_LOADING 1
+#define IDS_TRANSFERRING_PERCENT 2
+#define IDS_DONE 3
+#define IDS_ERROR 4
+#define IDS_TRANSFERRING 5
+#define IDD_INVISI 9
+#define IDR_ZEN_ICON 103
+#define IDS_NULLSOFT_CREATIVE_NJB 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 108
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Portable/pmp_njb/resources/zenIcon.png b/Src/Plugins/Portable/pmp_njb/resources/zenIcon.png
new file mode 100644
index 00000000..7352c626
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/resources/zenIcon.png
Binary files differ
diff --git a/Src/Plugins/Portable/pmp_njb/version.rc2 b/Src/Plugins/Portable/pmp_njb/version.rc2
new file mode 100644
index 00000000..7ff08345
--- /dev/null
+++ b/Src/Plugins/Portable/pmp_njb/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,57,0,0
+ 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 Portable Device Plug-in"
+ VALUE "FileVersion", "0,57,0,0"
+ VALUE "InternalName", "Nullsoft Creative NJB"
+ VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "pmp_njb.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END