/* Copyright (C) 2000 Andy Sherwood (enigmax@home.com) Fixes and extensions added by tom kistner (tom@duncanthrax.net): - smart media support - GetSong() - Several bugfixes see http://duncanthrax.net/riofxp/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Rio500.cpp : Implementation of CRio500 #include "stdafx.h" #include "Rio500Remix.h" #include "Rio500.h" #include "Folder.h" #include "Song.h" #include #include #include "usb100.h" // DDK #include // DDK #pragma comment(lib, "setupapi.lib") // DDK #pragma comment(lib, "WS2_32.LIB") // for use when checking for the mp3 frame header #define STR_RIFF (((((('R' << 8) | 'I') << 8) | 'F') << 8) | 'F') #define STR_WAVE (((((('W' << 8) | 'A') << 8) | 'V') << 8) | 'E') #define STR_MPEG (((((('M' << 8) | 'P') << 8) | 'E') << 8) | 'G') #define STR_fmt (((((('f' << 8) | 'm') << 8) | 't') << 8) | ' ') #define STR_fact (((((('f' << 8) | 'a') << 8) | 'c') << 8) | 't') #define STR_data (((((('d' << 8) | 'a') << 8) | 't') << 8) | 'a') // NOTE: You must have the DDK inc\ and libfre\i386\ directories setup in your // Visual C++ directories for this compile properly.. Sorry, I don't // really care for doing it this way, if someone knows a better way, // please let me know.. static GUID GUID_CLASS_RIO500 = { 0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed }; void DBPRINTF(char *sFormat, ...) { int i; static char buf[1024]; va_list va; va_start(va, sFormat); i = vsprintf(buf, sFormat, va); va_end(va); printf(buf); OutputDebugString(buf); } ///////////////////////////////////////////////////////////////////////////// // CRio500 //********************************************************************************************* // Description: Constructor for CRio500 // // Returns: Nothing // //********************************************************************************************* CRio500::CRio500() : m_hRio500(INVALID_HANDLE_VALUE), // Handle to device -. m_hRio500Read(INVALID_HANDLE_VALUE), // Bulk read handle --> Same w/Diamond driver m_hRio500Write(INVALID_HANDLE_VALUE), // Bulk write handle -' m_bsFontName(_T("MS Sans Serif")), // Default font name m_lFontSize(8) // Default font size { } //********************************************************************************************* // Description: Final construction for sub-object initialization (n/a right now) // // Returns: S_OK // //********************************************************************************************* HRESULT CRio500::FinalConstruct() { return S_OK; } //********************************************************************************************* // Description: Destructor for CRio500 // // Returns: Nothing // //********************************************************************************************* CRio500::~CRio500() { // Shutdown communications. We always want to shut down gracefully, even if an // exception occurs. EndComm(); // Make sure the driver is closed. CloseDriver(); } //********************************************************************************************* // Description: Boiler-plate code from wizard // // Returns: S_OK Extended error information is available for the specified interface // S_FALSE No extended error information for the specified interface // //********************************************************************************************* STDMETHODIMP CRio500::InterfaceSupportsErrorInfo( REFIID riid) // [i] Interface to check for extended error information { static const IID* arr[] = { &IID_IRio500 }; for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++) { // if (::InlineIsEqualGUID(*arr[i], riid)) return S_OK; } return S_FALSE; } //********************************************************************************************* // Description: Open the device driver and start communications with the Rio 500 device // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::Open() { HRESULT hr = S_OK; int iResult = 0; ATLTRACE("Open\n"); try { // Try to open the driver... iResult = OpenDriver(); if (iResult > 0) { // try alternative method (windows 98/se/me) iResult = OpenDriverRaw(); } Sleep(50); // didn't work at all .. bahhh ! another try can't be wrong if (iResult > 0) { iResult = OpenDriver(); if (iResult > 0) { // try alternative method (windows 98/se/me) iResult = OpenDriverRaw(); } } if (iResult > 0) throw CLocalException(RIO_E_COULDNT_OPEN_DEVICE, L"Rio not found. Plugged in ? Else see README.TXT for info"); // ... and start communications if (!StartComm()) throw CLocalException(RIO_E_COMM_START_ERROR, L"Could not start communications with the Rio!"); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Shutdown communications and close the device driver // // Returns: S_OK // //********************************************************************************************* STDMETHODIMP CRio500::Close() { ATLTRACE("Close\n"); // Shutdown communications EndComm(); // Close device driver CloseDriver(); return S_OK; } // Get presence of flash card // returns S_OK when succesful // puts "1" in pVal if card found, "0" if not STDMETHODIMP CRio500::get_ExtFlash(long *pVal) { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // detect flash card *pVal = (long) ((SendCommand(REQ_NOP, 0x0000, 0x0000) & 0x40000000) >> 30); // Wait a bit... NOP(); NOP(); } catch (CLocalException e) { hr = e.GetErrorCode(); } return S_OK; } //********************************************************************************************* // Description: Get available memory on the Rio // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::get_AvailableMemory( long lFlash, long *pVal) // [o] Number of bytes free on the Rio { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Query Rio for free memory *pVal = (long)SendCommand(REQ_QUERY_FREE_MEM, 0x0000, lFlash); // Wait a bit... NOP(); NOP(); } catch (CLocalException e) { hr = e.GetErrorCode(); } return S_OK; } //********************************************************************************************* // Description: Get the Firmware version // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::get_FirmwareVersion( long *pVal) // [o] Firmware Version of the Rio { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Query Rio for free memory *pVal = (long)SendCommand(REQ_FIRMWARE, 0x0000, 0x0000) & 0xFFFF; // Wait a bit... NOP(); NOP(); } catch (CLocalException e) { hr = e.GetErrorCode(); } return S_OK; } //********************************************************************************************* // Description: Format the Rio's flash memory erasing all songs and folders on the Rio // // NOTE: You must add a folder IMMEDIATELY after formatting the Rio! // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::Format(long lFlash) { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Send the format command if (lFlash == 0) { if (SendCommand(REQ_RIO_FORMAT_DEVICE, 0x2185, lFlash) != RSTATUS_OK) throw CLocalException(RIO_E_FORMAT_FAILED, L"Could not format internal flash memory!"); } else { if (SendCommand(REQ_RIO_FORMAT_DEVICE, 0x2185, lFlash) != RSTATUS_OK) throw CLocalException(RIO_E_FORMAT_FAILED, L"Could not format external flash memory!"); } Sleep(1000); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Add a folder to the Rio. Only the folder name needs to be specified. The // folder name will be rendered with the information from the font properties // // NOTE: Only 8 folders are supported right now. But this should be more than enough // for normal usage. It's not like there's a gigabyte of memory on the thing.. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::AddFolder( BSTR bsName, long lFlash) // [i] Name of folder to add to the Rio { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Build a folder object CComObjectStack Folder; _variant_t vBitmap; WORD wBlocks = 0; // Put in the name.. Folder.put_Name(bsName); // Build a bitmap for this folder name in a variant if (!BuildTextBitmapInVariant(bsName, vBitmap, wBlocks)) throw CLocalException(RIO_E_CREATE_BITMAP_FAILED, L"Could not create bitmap for folder text!"); // Stick the bitmap in the folder Folder.put_Bitmap(vBitmap); Folder.put_BitmapBlocks(wBlocks); // Do the actual work hr = AddFolderEx(&Folder, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Add a folder to Rio. User must build a COMPLETE folder object, and pass it // to this function. See the IFolder object for more information on the // properties this object must contain. // // NOTE: Only 8 folders are supported right now. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::AddFolderEx( IFolder* pFolder, long lFlash) // [i] Pointer to folder object { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); FOLDERENTRYLIST Folders; // If this is not the first folder, read the existing folder entries if (!IsFirstFolder(lFlash)) Folders = ReadFolderEntries(lFlash); // Get number of folders DWORD dwNumFolders = Folders.size(); // Make sure there are less than 8 folders already present if (dwNumFolders > 7) throw CLocalException(RIO_E_MAX_FOLDERS, L"Can not have more than 8 folders!"); // Prepare a new folder entry SRioFolderEntry Folder; ZeroMemory(&Folder, sizeof(SRioFolderEntry)); // Builf the folder entry from the folder object passed if (!BuildFolderEntry(pFolder, Folder)) throw CLocalException(RIO_E_NEW_FOLDER_FAILED, L"Could not build new folder entry!"); // Wait a bit.. NOP(); NOP(); // Allocate an empty folder block if (!AllocateEmptyFolderBlock(dwNumFolders, lFlash)) throw CLocalException(RIO_E_FOLDER_ALLOC_FAILED, L"Could not allocate empty folder block!"); // Wait a bit.. NOP(); NOP(); // Find out where the new empty folder block is DWORD dwSendBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Put empty folder block location in current folder Folder.wOffset = (WORD)dwSendBlockLoc; // Add the folder entry to the list of folders Folders.push_back(Folder); // Write all the folder entries back to the Rio if (!WriteFolderEntries(Folders, lFlash)) throw CLocalException(RIO_E_FOLDER_WRITE_FAILED, L"Could not write folder entries to device!"); // Wait a bit NOP(); NOP(); // Find out where the folder block was written to DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell the Rio where to find the folder block if (!SendFolderLocation(dwFolderBlockLoc, dwNumFolders,lFlash)) throw CLocalException(RIO_E_COULDNT_SEND_FOLDER_LOC, L"Could not send folder location to device!"); // End folder upload SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Rename an existing folder. The folder's name bitmap will be built with the // information in the font properties. // // WARNING: This is not prepared for smart media support yet ! // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::RenameFolder( long lFolderNum, // [i] Folder number to rename BSTR bsNewName) // [i] New name for folder { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read all the folders FOLDERENTRYLIST Folders = ReadFolderEntries(0); //FIXME // Make sure we have a valid folder number if (lFolderNum >= Folders.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder number was specified!"); // Build the bitmap for the folder name BuildTextBitmap(bsNewName, Folders[lFolderNum].Bitmap.Image, Folders[lFolderNum].Bitmap.wNumBlocks); // Convert the BSTR name to an ASCII name... if (!WideCharToMultiByte(CP_ACP, NULL, bsNewName, SysStringLen(bsNewName), Folders[lFolderNum].szName1, 362, NULL, NULL)) { DWORD dwError = GetLastError(); throw CLocalException(RIO_E_STRING_ERROR, L"Couldn't convert name to ASCII string!"); } // ... and copy it to the secondary name location *Folders[lFolderNum].szName2 = NULL; strncat(Folders[lFolderNum].szName2, Folders[lFolderNum].szName1, 127); // Write the folder entries back to the Rio WriteFolderEntries(Folders, 0); //FIXME // Wait a bit... NOP(); NOP(); // Get the new folder entries location DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell the Rio where they're at SendFolderLocation(dwFolderBlockLoc, lFolderNum, 0); //FIXME // End folder transfer SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, 0x0000); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Delete a folder and all songs in that folder // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::DeleteFolder( long lFolderNum, long lFlash) // [i] Folder number to delete { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read the existing folder list FOLDERENTRYLIST Folders = ReadFolderEntries(lFlash); int i; // Make sure we have a valid folder number if (lFolderNum >= Folders.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder number was specified!"); // Read all songs in this folder... SONGENTRYLIST Songs = ReadSongEntries(Folders[lFolderNum], lFolderNum, lFlash); // ... and delete them. I don't know why this works, but it does. for (i = 0; i < Songs.size(); i++) SendCommand(REQ_SET_WRITE_ADDRESS, ((lFolderNum << 8) | i), lFlash); // Delete the folder SendCommand(REQ_SET_WRITE_ADDRESS, (lFolderNum << 8), lFlash); // Remove the folder entry from the folder list Folders.erase(Folders.begin() + lFolderNum); // Write the folder entries back to the Rio WriteFolderEntries(Folders,lFlash); // Wait a bit.. NOP(); NOP(); // Get the new folder entries location DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell the Rio where they're at SendFolderLocation(dwFolderBlockLoc, lFolderNum, lFlash); // End folder transfer SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Enumerate the folders on the device and return them as a COM collection // // NOTE: Only the number and name properties are valid in the returned folders. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::get_Folders( long lFlash, IFolders** ppFolders) // [o] COM collection containing Folder objects. { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read the folder list FOLDERENTRYLIST FolderEntries = ReadFolderEntries(lFlash); // Create a new folder collection if (FAILED(hr = CoCreateInstance(__uuidof(Folders), NULL, CLSCTX_ALL, __uuidof(IFolders), (void**)ppFolders))) throw CLocalException(hr, L"Folders class not registered?"); // Add each folder to the collection for (long i = 0; i < FolderEntries.size(); i++) { CComPtr pFolder; // Create a new folder object if (FAILED(hr = pFolder.CoCreateInstance(__uuidof(Folder)))) throw CLocalException(hr, L"Folder class not registered?"); // Put the number and name in it. pFolder->put_Number(i); pFolder->put_Name(CComBSTR(FolderEntries[i].szName1)); // Add it to the collection (*ppFolders)->AddFolder(pFolder); } } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Add a song (MP3) to the Rio. User must specify folder to add the song to, // the name of the song, and the file to upload. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::AddSong( long lFolder, // [i] Folder to add the song to BSTR bsName, // [i] Name of the song BSTR bsSongFileName, long lFlash) // [i] File (MP3) to upload { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Build a new song object CComObjectStack Song; _variant_t vBitmap; WORD wBlocks = 0; // Put the name, song file name and folder number in the object Song.put_Name(bsName); Song.put_SongFileName(bsSongFileName); Song.put_Folder(lFolder); // Build a bitmap for the song name if (!BuildTextBitmapInVariant(bsName, vBitmap, wBlocks)) throw CLocalException(RIO_E_CREATE_BITMAP_FAILED, L"Could not create bitmap for song text!"); // Put it in the object Song.put_Bitmap(vBitmap); Song.put_BitmapBlocks(wBlocks); // Do the actual work... hr = AddSongEx(&Song, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Add a song (MP3) to the Rio. User must specify a valid Song object, with ALL // properties filled in appropriately. See the ISong object for more information // on the properties this object must contain. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::AddSongEx( ISong* pSong, long lFlash) // [i] Pointer to a song object to add { HRESULT hr = S_OK; CComBSTR bsSongFileName; HANDLE hFile = INVALID_HANDLE_VALUE; BYTE* pBlock = NULL; BSTR bsName = NULL; VARIANT vBitmap; try { // Make sure the driver is open if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Initialize the variant which will hold the song name bitmap VariantInit(&vBitmap); // Get the song file name pSong->get_SongFileName(&bsSongFileName); // Try to open the file hFile = CreateFile(_bstr_t(bsSongFileName), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); // Make sure it opened successfully if (hFile == INVALID_HANDLE_VALUE) throw CLocalException(RIO_E_FILE_NOT_FOUND, L"File not found"); // Build a new song block pBlock = new BYTE[RIO_HUGEBLOCKSIZE]; DWORD dwFileSize; // Get the file size of the song dwFileSize = GetFileSize(hFile, NULL); DWORD dwMemLeft = 0; // Get free memory on the Rio. dwMemLeft = (long)SendCommand(REQ_QUERY_FREE_MEM, 0x0000, lFlash); // Wait a bit.. NOP(); NOP(); // Make sure there's enough memory for the specified song if (dwMemLeft < dwFileSize) throw CLocalException(RIO_E_NOT_ENOUGH_MEMORY, L"Not enough free space for this song!"); // Read the folder list from the Rio FOLDERENTRYLIST Folders = ReadFolderEntries(lFlash); DWORD dwFolderNum = 0; // Get the destination folder number pSong->get_Folder((long*)&dwFolderNum); // Make sure we have a valid folder number if (dwFolderNum > Folders.size()-1) throw CLocalException(RIO_E_INVALID_FOLDER, L"Invalid folder number specified"); // Calculate number of data blocks and remainder size DWORD dwNumBlocks = dwFileSize / RIO_HUGEBLOCKSIZE; DWORD dwAddBlocks = (dwFileSize % RIO_HUGEBLOCKSIZE) / RIO_BIGBLOCKSIZE; DWORD dwRemainder = (dwFileSize % RIO_HUGEBLOCKSIZE) % RIO_BIGBLOCKSIZE; DWORD dwHowMuchReq = 0; DWORD dwHowMuch = 0; DWORD dwNumRead = 0; DWORD dwWritten = 0; // Who knows... SendCommand(REQ_MYSTERY, 0xFFFF, lFlash); // Write out all the blocks... for (DWORD b = 0; b <= dwNumBlocks; b++) { // We want to write 1MB blocks or some c*64kB block if less than 1MB remains dwHowMuchReq = (b < dwNumBlocks ? RIO_HUGEBLOCKSIZE : dwAddBlocks*RIO_BIGBLOCKSIZE); if (dwHowMuchReq) { dwHowMuch = SendCommand(REQ_WRITE_TO_USB, dwHowMuchReq >> 16, 0); // no flash // Make sure the Rio agrees.. if (dwHowMuch != dwHowMuchReq) throw CLocalException(RIO_E_COULDNT_ACCEPT_DATA, L"Rio could not accept data!"); // Read the data from the file ReadFile(hFile, pBlock, dwHowMuch, &dwNumRead, NULL); // Make sure we got what we expect if (dwNumRead != dwHowMuch) throw CLocalException(RIO_E_SHORT_READ, L"Short read!"); // Write it out to the Rio dwWritten = 0; for (int i=0; i<(b < dwNumBlocks ? RIO_HUGEBLOCKSIZE/RIO_BIGBLOCKSIZE : dwAddBlocks); i++) dwWritten += BulkWrite(pBlock+i*RIO_BIGBLOCKSIZE, RIO_BIGBLOCKSIZE); if (dwWritten != dwHowMuch) throw CLocalException(RIO_E_WRITE_FAILED, L"Could not write data to Rio!"); //char ss[20]; //sprintf(ss,"%d", dwAddBlocks); MessageBox(NULL, ss, NULL, MB_OK); //sprintf(ss,"%d",dwWritten); MessageBox(NULL, ss, NULL, MB_OK); // Wait a bit.. NOP(); NOP(); // Fire an update event every 1MB if (dwNumBlocks) { if (Fire_OnUploadUpdate((b * 100) / dwNumBlocks) == E_ABORT) throw CLocalException(RIO_E_USER_ABORTED, L"User aborted transfer!"); } } } // If there's a remainder, send it.. if (dwRemainder) { // Tell the Rio we have a partial block for it.. dwHowMuch = SendCommand(REQ_WRITE_TO_USB, 0x0000, dwRemainder); // Make sure it agrees if (dwHowMuch != dwRemainder) throw CLocalException(RIO_E_COULDNT_ACCEPT_DATA, L"Rio could not accept data!"); // Read the data from the file ReadFile(hFile, pBlock, dwRemainder, &dwNumRead, NULL); // Make sure we got what we expect if (dwNumRead != dwRemainder) throw CLocalException(RIO_E_SHORT_READ, L"Short read!"); // Write it to the Rio if (BulkWrite(pBlock, dwNumRead) != dwNumRead) throw CLocalException(RIO_E_WRITE_FAILED, L"Could not write data to Rio!"); // Wait a bit.. NOP(); NOP(); } // Fire an final update event Fire_OnUploadUpdate(100); // Get the location of the song DWORD dwSongLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ?? // Prepare a new song entry SRioSongEntry NewSong; ZeroMemory(&NewSong, sizeof(SRioSongEntry)); // reset to begining of the file SetFilePointer(hFile,0,NULL,FILE_BEGIN); // Fill in the data for the song NewSong.wOffset = WORD(dwSongLoc); NewSong.dwLength = dwFileSize; NewSong.wDunno3 = 0x0020; NewSong.dwMP3Sig = (DWORD)GetFrameHeader(hFile); NewSong.dwTime = time(NULL); // Get song name into temporary variable pSong->get_Name(&bsName); // Convert song name from BSTR to ASCII... if (!WideCharToMultiByte(CP_ACP, NULL, bsName, SysStringLen(bsName), NewSong.szName1, 362, NULL, NULL)) { DWORD dwError = GetLastError(); throw CLocalException(RIO_E_STRING_ERROR, L"Couldn't convert name to ASCII string!"); } // ... and copy to secondary song name strncat(NewSong.szName2, NewSong.szName1, 127); // Get song name bitmap pSong->get_BitmapBlocks(&NewSong.Bitmap.wNumBlocks); pSong->get_Bitmap(&vBitmap); // Make sure the bitmap is the right type (array of UI1) if (vBitmap.vt != (VT_ARRAY|VT_UI1)) throw CLocalException(RIO_E_BITMAP_INVALID, L"Invalid variant passed as bitmap!"); BYTE* pBitmap = NULL; long lUBound = 0; // Get size of array SafeArrayGetUBound(vBitmap.parray, 1, &lUBound); // Make sure it's the correct size if (lUBound < 1535) throw CLocalException(RIO_E_NOT_ENOUGH_BITMAP_DATA, L"Not enough data for complete bitmap!"); // Get access to the bitmap data SafeArrayAccessData(vBitmap.parray, (void**)&pBitmap); // Copy it into the song entry memcpy(NewSong.Bitmap.Image, pBitmap, RIO_BITMAPSIZE); // Unlock the safe array SafeArrayUnaccessData(vBitmap.parray); // Read the song list for this folder SONGENTRYLIST Songs = ReadSongEntries(Folders[dwFolderNum], dwFolderNum, lFlash); // Add this song entry to the list Songs.push_back(NewSong); // Write the song entries back to the Rio WriteSongEntries(dwFolderNum, Songs, lFlash); // Wait a bit.. NOP(); NOP(); // Get location of song entries DWORD dwSongBlockOfs = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // And store them in the folder.. Folders[dwFolderNum].wOffset = WORD(dwSongBlockOfs); Folders[dwFolderNum].wFirstFreeEntryOfs += sizeof(SRioFolderEntry); // Write out folder entries WriteFolderEntries(Folders, lFlash); // Wait a bit.. NOP(); NOP(); // Get folder location DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell Rio where the folders are SendFolderLocation(dwFolderBlockLoc, dwFolderNum,lFlash); // End folder transfer SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } // Erase the variant we allocated for the bitmap VariantClear(&vBitmap); // Free BSTR for song name if (bsName) SysFreeString(bsName); // Free song block data if (pBlock) delete pBlock; // Close the song file if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); return hr; } //********************************************************************************************* // Description: Rename an existing song. The song's name bitmap will be built with the // information in the font properties. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::RenameSong( long lFolderNum, // [i] Folder number of song to be renamed long lSongNum, // [i] Song number of song to be renamed BSTR bsNewName, long lFlash) // [i] New name of song { HRESULT hr = S_OK; try { // Make sure the driver is opened.. if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read the folder entries list FOLDERENTRYLIST Folders = ReadFolderEntries(lFlash); // Make sure we have a valid folder number if (lFolderNum >= Folders.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder was specified!"); // Read song entries from folder SONGENTRYLIST Songs = ReadSongEntries(Folders[lFolderNum], lFolderNum, lFlash); // Build a new name bitmap BuildTextBitmap(bsNewName, Songs[lSongNum].Bitmap.Image, Songs[lSongNum].Bitmap.wNumBlocks); // Convert BSTR'd name to ASCII string if (!WideCharToMultiByte(CP_ACP, NULL, bsNewName, SysStringLen(bsNewName), Songs[lSongNum].szName1, 362, NULL, NULL)) { DWORD dwError = GetLastError(); throw CLocalException(RIO_E_STRING_ERROR, L"Couldn't convert name to ASCII string!"); } // BUGFIX - tk // add 0x0 Songs[lSongNum].szName1[SysStringLen(bsNewName)] = 0x0; // And copy to secondary name *Songs[lSongNum].szName2 = NULL; strncpy(Songs[lSongNum].szName2, Songs[lSongNum].szName1, 127); // Write song entries back to Rio WriteSongEntries(lFolderNum, Songs, lFlash); // Wait a bit NOP(); NOP(); // Get location of song entries DWORD dwSongBlockOfs = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Write to folder Folders[lFolderNum].wOffset = WORD(dwSongBlockOfs); //Folders[lFolderNum].wFirstFreeEntryOfs += sizeof(SRioFolderEntry); // Write folders back to Rio WriteFolderEntries(Folders, lFlash); // Wait a bit.. NOP(); NOP(); // Get location of folder entries.. DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell Rio where the folders are SendFolderLocation(dwFolderBlockLoc, lFolderNum, lFlash); // End folder transfer SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Delete a song from the specified folder // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::DeleteSong( long lFolderNum, // [i] Folder number of song to delete long lSongNum, long lFlash) // [i] Song number to delete { HRESULT hr = S_OK; try { // Make sure the driver is open if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read folder entries FOLDERENTRYLIST Folders = ReadFolderEntries(lFlash); // Make sure we have a valid folder if (lFolderNum >= Folders.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder number was specified!"); // Read song entries SONGENTRYLIST Songs = ReadSongEntries(Folders[lFolderNum], lFolderNum, lFlash); // Make sure we have a valid song number if (lSongNum >= Songs.size()) throw CLocalException(RIO_E_INVALID_SONG, L"An invalid song number was specified!"); // Remove song from the list Songs.erase(Songs.begin() + lSongNum); // Remove song from Rio. This works (for some reason) SendCommand(REQ_SET_WRITE_ADDRESS, ((lFolderNum << 8) | lSongNum), lFlash); // Write song entries back to Rio WriteSongEntries(lFolderNum, Songs, lFlash); // Wait a bit.. NOP(); NOP(); // Get location of song entries DWORD dwSongBlockOfs = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Write to folder.. Folders[lFolderNum].wOffset = WORD(dwSongBlockOfs); Folders[lFolderNum].wFirstFreeEntryOfs -= 0x800; //Folders[lFolderNum].wFirstFreeEntryOfs += sizeof(SRioFolderEntry); // Write folders back to Rio WriteFolderEntries(Folders,lFlash); // Wait a bit.. NOP(); NOP(); // Get location of folders DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell Rio where the folders are SendFolderLocation(dwFolderBlockLoc, lFolderNum,lFlash); // End folder transfer SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Swap a song entry. This is meant to ease UI development. For example, this // method would be called when the user rearranges songs on the device. // Especially in a drag and drop manner. There are probably some other methods // like this that will need to be written, but this was the only one I could come // up with right now. // // NOTE: You can only swap songs in a SINGLE folder. It should probably handle moving // songs between folders.. Oh well. Maybe I'll get around to that later. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::SwapSong( long lFolderNum, // [i] Folder number of songs to swap long lSongNum1, // long lSongNum2, long lFlash) { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read the folder entries FOLDERENTRYLIST Folders = ReadFolderEntries(lFlash); // Make sure we have a valid folder number if (lFolderNum >= Folders.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder number was specified!"); // Read song entries SONGENTRYLIST Songs = ReadSongEntries(Folders[lFolderNum], lFolderNum, lFlash); // Make sure BOTH song numbers are valid.. if (lSongNum1 >= Songs.size() || lSongNum2 >= Songs.size()) throw CLocalException(RIO_E_INVALID_SONG, L"An invalid song number was specified!"); // Temporary song entry SRioSongEntry Temp; // Swap the two entries memcpy(&Temp, &Songs[lSongNum1], sizeof(SRioSongEntry)); memcpy(&Songs[lSongNum1], &Songs[lSongNum2], sizeof(SRioSongEntry)); memcpy(&Songs[lSongNum2], &Temp, sizeof(SRioSongEntry)); // Write the song entries back to the Rio WriteSongEntries(lFolderNum, Songs, lFlash); // Wait a bit.. NOP(); NOP(); // Get location of song entries DWORD dwSongBlockOfs = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Write them to the folder Folders[lFolderNum].wOffset = WORD(dwSongBlockOfs); //Folders[lFolderNum].wFirstFreeEntryOfs += sizeof(SRioFolderEntry); // Write the folder entries back to the Rio WriteFolderEntries(Folders, lFlash); // Wait a bit NOP(); NOP(); // Get the folder entries location DWORD dwFolderBlockLoc = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0x0000, 0x0000); // no lFlash necessary ? // Tell the Rio where it is SendFolderLocation(dwFolderBlockLoc, lFolderNum, lFlash); // End folder transfer SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0000, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Enumerate the songs in a folder on the Rio. Return them as a COM collection // // NOTE: Only the number and name properties are valid in the returned songs. // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::get_Songs( long lFlash, long lFolderNum, // [i] Folder number to get songs for ISongs** ppSongs) // [o] Collection of Song objects { HRESULT hr = S_OK; try { // Make sure the driver is opened if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read the folder list FOLDERENTRYLIST FolderEntries = ReadFolderEntries(lFlash); // Make sure we have a valid folder number if (lFolderNum >= FolderEntries.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder number was specified!"); // Read the song entries SONGENTRYLIST SongEntries = ReadSongEntries(FolderEntries[lFolderNum], lFolderNum, lFlash); // Create a Song collection if (FAILED(hr = CoCreateInstance(__uuidof(Songs), NULL, CLSCTX_ALL, __uuidof(ISongs), (void**)ppSongs))) throw CLocalException(hr, L"Songs class not registered?"); // Create a Song object for each song entry for (long i = 0; i < SongEntries.size(); i++) { CComPtr pSong; // Creat the song object if (FAILED(hr = pSong.CoCreateInstance(__uuidof(Song)))) throw CLocalException(hr, L"Song class not registered?"); // Put the number and name in the object pSong->put_Number(i); pSong->put_Name(CComBSTR(SongEntries[i].szName1)); pSong->put_Size(SongEntries[i].dwLength); // Add it to the collection (*ppSongs)->AddSong(pSong); } } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } //********************************************************************************************* // Description: Get the font name to be used when building text bitmaps of folder and song // names // // NOTE: All font methods may be replaced in the future to use the IFont interface // instead. I'm still thinking about it... // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::get_FontName( BSTR *pVal) // [o] Name of font { *pVal = m_bsFontName.copy(); return S_OK; } //********************************************************************************************* // Description: Set the font name to be used when building text bitmaps of folder and song // names // // NOTE: All font methods may be replaced in the future to use the IFont interface // instead. I'm still thinking about it... // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::put_FontName( BSTR newVal) // [i] Name of font { m_bsFontName = newVal; return S_OK; } //********************************************************************************************* // Description: Get the font size to be used when building text bitmaps of folder and song // names // // NOTE: All font methods may be replaced in the future to use the IFont interface // instead. I'm still thinking about it... // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::get_FontSize( long *pVal) // [o] Point size of font { *pVal = m_lFontSize; return S_OK; } //********************************************************************************************* // Description: Set the font size to be used when building text bitmaps of folder and song // names // // NOTE: All font methods may be replaced in the future to use the IFont interface // instead. I'm still thinking about it... // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::put_FontSize( long newVal) // [i] Point size of font { m_lFontSize = newVal; return S_OK; } //********************************************************************************************* // Description: Read song from Rio. Added by Tom Kistner // // Returns: HRESULT S_OK, or Error code indicating the type of failure // //********************************************************************************************* STDMETHODIMP CRio500::GetSong( long lFolderNum, // [i] Folder number of song to get long lSongNum, // [i] Song number to get BSTR bsSongFileName, long lFlash) // [i] Filename to download to { HRESULT hr = S_OK; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD myBytesWritten,dwFolderBlockOffset; BYTE* pBlock = NULL; WORD wOffsetBuffer; try { // a memory block for our data pBlock = new BYTE[RIO_HUGEBLOCKSIZE]; // Make sure the driver is open if (!IsDriverOpened()) throw CLocalException(RIO_E_DRIVER_NOT_OPEN, L"Driver not open!"); // Read folder entries FOLDERENTRYLIST Folders = ReadFolderEntries(lFlash); // Make sure we have a valid folder if (lFolderNum >= Folders.size()) throw CLocalException(RIO_E_INVALID_FOLDER, L"An invalid folder number was specified!"); // Read song entries SONGENTRYLIST Songs = ReadSongEntries(Folders[lFolderNum], lFolderNum, lFlash); // Make sure we have a valid song number if (lSongNum >= Songs.size()) throw CLocalException(RIO_E_INVALID_SONG, L"An invalid song number was specified!"); // Try to open the file hFile = CreateFile(_bstr_t(bsSongFileName), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // Make sure it opened successfully if (hFile == INVALID_HANDLE_VALUE) throw CLocalException(RIO_E_FILE_NOT_FOUND, L"Cannot open file"); // ********************************* // TODO: file size chk // ********************************* // now we do the trick .. change folder offset with the song offset // then write folders to the rio // save folder 0's original offset wOffsetBuffer = Folders[0].wOffset; Folders[0].wOffset = Songs[lSongNum].wOffset; // write folders WriteFolderEntries(Folders, lFlash); // some black magic SendCommand(REQ_NOP, 0, 0); SendCommand(REQ_NOP, 0, 0); dwFolderBlockOffset = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0, 0); // Tell Rio where the root folder block is. SendFolderLocation(dwFolderBlockOffset, Folders.size(), lFlash); // black magic again SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0, lFlash); // setup variables and communication DWORD dwReadSizeReq = 0; DWORD dwReadSize = 0; DWORD dwCount = 0; WORD wBlocksToRead = Songs[lSongNum].dwLength / RIO_HUGEBLOCKSIZE; WORD wBlocksAdd = (Songs[lSongNum].dwLength % RIO_HUGEBLOCKSIZE) / RIO_BLOCKSIZE; WORD wRemainder = (Songs[lSongNum].dwLength % RIO_HUGEBLOCKSIZE) % RIO_BLOCKSIZE; SendCommand(REQ_QUERY_FOLDER_BLOCK, 0xff, lFlash); // read the file for (WORD b = 0; b <= wBlocksToRead; b++) { // char ss[20]; // sprintf(ss,"%d",SendCommand(REQ_READ_FROM_USB, 0x10, 0x0)); // MessageBox(NULL, ss, NULL, MB_OK); dwReadSizeReq = (b < wBlocksToRead ? RIO_HUGEBLOCKSIZE : wBlocksAdd*RIO_BLOCKSIZE); if (dwReadSizeReq) { dwReadSize = SendCommand(REQ_READ_FROM_USB, dwReadSizeReq >> 16, dwReadSizeReq & 0xFFFF); // if (b == wBlocksToRead) // { // sprintf(ss,"%d",dwReadSizeReq); // MessageBox(NULL, ss, NULL, MB_OK); // } if (dwReadSize != dwReadSizeReq) throw CLocalException(RIO_E_COULDNT_ACCEPT_DATA, L"Rio could not accept data!"); dwCount = 0; for (int i=0; i < (b < wBlocksToRead ? RIO_HUGEBLOCKSIZE/RIO_BLOCKSIZE : wBlocksAdd); i++) dwCount += BulkRead(pBlock+i*RIO_BLOCKSIZE,RIO_BLOCKSIZE); // sprintf(ss,"%d",wCount); MessageBox(NULL, ss, NULL, MB_OK); if (dwCount != dwReadSize) throw CLocalException(RIO_E_SHORT_READ, L"Short Read!"); WriteFile(hFile,pBlock,dwReadSize,&myBytesWritten,NULL); NOP(); NOP(); } } // read remainder if (wRemainder) { SendCommand(REQ_READ_FROM_USB, 0x0, wRemainder); dwCount = BulkRead(pBlock, wRemainder); if (dwCount != wRemainder) throw CLocalException(RIO_E_SHORT_READ, L"Short Read in Remainder !"); WriteFile(hFile,pBlock,dwCount,&myBytesWritten,NULL); NOP(); NOP(); } // Close the song file if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); // Free song block data if (pBlock) delete pBlock; // restore correct folder offset Folders[0].wOffset = wOffsetBuffer; // write folders WriteFolderEntries(Folders, lFlash); // some black magic SendCommand(REQ_NOP, 0, 0); SendCommand(REQ_NOP, 0, 0); dwFolderBlockOffset = SendCommand(REQ_QUERY_OFFSET_LAST_WRITE, 0, 0); // Tell Rio where the root folder block is. SendFolderLocation(dwFolderBlockOffset, Folders.size(), lFlash); // black magic again SendCommand(REQ_END_FOLDER_TRANSFERS, 0x0, lFlash); } catch (CLocalException e) { hr = e.GetErrorCode(); } return hr; } /* *\ ******************************************************************************************** ******************************************************************************************** ******************************************************************************************** All methods below this point are internal to the CRio500 object and are not exposed through automation methods ******************************************************************************************** ******************************************************************************************** ******************************************************************************************** \* */ //********************************************************************************************* // Description: Scan the USB bus for the Rio 500 and open the device driver. This code // was ripped almost completely from the BulkUSB test program in the DDK // // Changed this to more error return codes .. to get better user feedback --tk // // Returns: 0 Found, device driver opened // 1 Device found, but something went wrong setup // 2 No Device found in enumeration // 3 Device found, but driver could not be opened // //********************************************************************************************* int CRio500::OpenDriver() { HDEVINFO hHardwareDevInfo = INVALID_HANDLE_VALUE; TCHAR szRIO500Name[1024]; ZeroMemory(szRIO500Name, 1024); hHardwareDevInfo = SetupDiGetClassDevs(&GUID_CLASS_RIO500, NULL, NULL, (DIGCF_PRESENT | DIGCF_INTERFACEDEVICE)); bool bDone = false,bFoundDevice = false; SP_INTERFACE_DEVICE_DATA DeviceInfoData; ZeroMemory(&DeviceInfoData, sizeof(SP_INTERFACE_DEVICE_DATA)); DeviceInfoData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA); DWORD i = 0; PUSB_DEVICE_DESCRIPTOR pUSBDeviceInst; PUSB_DEVICE_DESCRIPTOR* ppUSBDevices = &pUSBDeviceInst; DWORD dwNumDevices = 4; *ppUSBDevices = NULL; while (!bDone) { dwNumDevices <<= 1; if (*ppUSBDevices == NULL) { *ppUSBDevices = (PUSB_DEVICE_DESCRIPTOR)malloc( dwNumDevices * sizeof(USB_DEVICE_DESCRIPTOR)); } else { *ppUSBDevices = (PUSB_DEVICE_DESCRIPTOR)realloc(*ppUSBDevices, dwNumDevices * sizeof(USB_DEVICE_DESCRIPTOR)); } pUSBDeviceInst = *ppUSBDevices + i; for (; i < dwNumDevices; i++) { if (SetupDiEnumDeviceInterfaces(hHardwareDevInfo, 0, &GUID_CLASS_RIO500, i, &DeviceInfoData)) { PSP_INTERFACE_DEVICE_DETAIL_DATA pFunctionClassDeviceData = NULL; DWORD dwPredictedLength = 0; DWORD dwRequiredLength = 0; // we got one bFoundDevice = true; // // allocate a function class device data structure to receive the // goods about this particular device. // SetupDiGetInterfaceDeviceDetail(hHardwareDevInfo, &DeviceInfoData, NULL, 0, &dwRequiredLength, NULL); dwPredictedLength = dwRequiredLength; pFunctionClassDeviceData = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc( dwPredictedLength); pFunctionClassDeviceData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); // // Retrieve the information from Plug and Play. // if (SetupDiGetInterfaceDeviceDetail(hHardwareDevInfo, &DeviceInfoData, pFunctionClassDeviceData, dwPredictedLength, &dwRequiredLength, NULL)) _tcscpy(szRIO500Name, pFunctionClassDeviceData->DevicePath); free(pFunctionClassDeviceData); pFunctionClassDeviceData = NULL; } else if (GetLastError() == ERROR_NO_MORE_ITEMS) { bDone = true; if (!bFoundDevice) return 2; break; } } } dwNumDevices = i; SetupDiDestroyDeviceInfoList(hHardwareDevInfo); if (*ppUSBDevices) free(*ppUSBDevices); // Return 1 if no driver found --tk if (!*szRIO500Name) return 1; // Open the device driver m_hRio500 = CreateFile( szRIO500Name, // Name of device driver's file object GENERIC_READ | GENERIC_WRITE, // Read/write access FILE_SHARE_READ | FILE_SHARE_WRITE, // Share read/write NULL, // No SECURITY_ATTRIBUTES structure OPEN_EXISTING, // No special create flags 0, // No special attributes NULL); // No template file // Debug Stuff, leave alone plz :) // //DWORD myBytesWritten; //HANDLE myFile = CreateFile(_bstr_t("TEST.TXT"), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, // CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //WriteFile(myFile,szRIO500Name,strlen(szRIO500Name),&myBytesWritten,NULL); //CloseHandle(myFile); // we should make sure it opens and return a spec. error --tk if (m_hRio500 == INVALID_HANDLE_VALUE) return 3; m_hRio500Read = m_hRio500Write = m_hRio500; // all ok ! --tk return 0; } //********************************************************************************************* // Description: VERY UGLY WORKAROUND .. but it should be ok for testing // // Returns: 0 - success // 1 - failed // //********************************************************************************************* int CRio500::OpenDriverRaw() { HKEY myRegKey; DWORD dwIndex = 0; TCHAR szBuffer[1024]; BOOL bDone = false; ZeroMemory(szBuffer, 1024); RegOpenKey(HKEY_LOCAL_MACHINE,"System",&myRegKey); RegOpenKey(myRegKey,"CurrentControlSet",&myRegKey); RegOpenKey(myRegKey,"Control",&myRegKey); RegOpenKey(myRegKey,"DeviceClasses",&myRegKey); RegOpenKey(myRegKey,"{a5dcbf10-6530-11d2-901f-00c04fb951ed}",&myRegKey); while(!(RegEnumKey(myRegKey,dwIndex,szBuffer,sizeof(szBuffer)) == ERROR_NO_MORE_ITEMS)) { int i = 0; for (i = 0;i < 1024; i++) { if (szBuffer[i] == 0x23) szBuffer[i] = 0x5C; }; szBuffer[20]=0x23; // Open the device driver m_hRio500 = CreateFile( szBuffer, // Name of device driver's file object GENERIC_READ | GENERIC_WRITE, // Read/write access FILE_SHARE_READ | FILE_SHARE_WRITE, // Share read/write NULL, // No SECURITY_ATTRIBUTES structure OPEN_EXISTING, // No special create flags 0, // No special attributes NULL); // No template file if (m_hRio500 != INVALID_HANDLE_VALUE) { m_hRio500Read = m_hRio500Write = m_hRio500; // make sure it is the rio .. :) if (StartComm()) { EndComm(); // all ok ! return 0; } else { CloseHandle(m_hRio500); }; }; dwIndex ++; }; // ok now we try the older CLSID ! ZeroMemory(szBuffer, 1024); dwIndex = 0; bDone = false; RegOpenKey(HKEY_LOCAL_MACHINE,"System",&myRegKey); RegOpenKey(myRegKey,"CurrentControlSet",&myRegKey); RegOpenKey(myRegKey,"Control",&myRegKey); RegOpenKey(myRegKey,"DeviceClasses",&myRegKey); RegOpenKey(myRegKey,"{4e183ba0-e87e-11d2-bde0-00902743f01d}",&myRegKey); while(!(RegEnumKey(myRegKey,dwIndex,szBuffer,sizeof(szBuffer)) == ERROR_NO_MORE_ITEMS)) { int i = 0; for (i = 0;i < 1024; i++) { if (szBuffer[i] == 0x23) szBuffer[i] = 0x5C; }; szBuffer[20]=0x23; // Open the device driver m_hRio500 = CreateFile( szBuffer, // Name of device driver's file object GENERIC_READ | GENERIC_WRITE, // Read/write access FILE_SHARE_READ | FILE_SHARE_WRITE, // Share read/write NULL, // No SECURITY_ATTRIBUTES structure OPEN_EXISTING, // No special create flags 0, // No special attributes NULL); // No template file if (m_hRio500 != INVALID_HANDLE_VALUE) { m_hRio500Read = m_hRio500Write = m_hRio500; // make sure it is the rio .. :) if (StartComm()) { EndComm(); // all ok ! return 0; } else { CloseHandle(m_hRio500); }; }; dwIndex ++; }; // nothing worked ... return 1; }; //********************************************************************************************* // Description: Close all the handles for the device driver // // Returns: Nothing // //********************************************************************************************* void CRio500::CloseDriver() { // Close device driver if (m_hRio500 != INVALID_HANDLE_VALUE) { CloseHandle(m_hRio500); m_hRio500 = INVALID_HANDLE_VALUE; } } //********************************************************************************************* // Description: Send the command to start communications with the Rio 500. // // Returns: true Communications established // false Could not contact the Rio // //********************************************************************************************* bool CRio500::StartComm() { SendCommand(REQ_START_USB_COMM, 0x0000, 0x0000); NOP(); NOP(); return true; } //********************************************************************************************* // Description: Send the command to stop communicating with the Rio. // // Returns: true Communications stopped // false Could not contact the Rio // //********************************************************************************************* bool CRio500::EndComm() { SendCommand(REQ_END_USB_COMM, 0x0000, 0x0000); NOP(); NOP(); return true; } //********************************************************************************************* // Description: Send a command to the Rio. A command consists of 2 WORDs written TO the Rio, // and one DWORD being read FROM the Rio. // // Returns: The DWORD returned by the Rio. // //********************************************************************************************* DWORD CRio500::SendCommand( RIO_REQUEST Req, // [i] A request code from the RIO_REQUEST enum WORD wValue, // [i] The nebulous "value" parameter... WORD wIndex) // [i] The nebulous "index" parameter... really external card .. :) { DWORD dwStatus; // Send the request.. if (!IORequest(DIR_IN, Req, wValue, wIndex, sizeof(DWORD), &dwStatus)) return RSTATUS_ERROR; // Return the result.. return dwStatus; } //********************************************************************************************* // Description: Perform an IO transaction with the device driver. // // Returns: true Transaction successful // false Transaction failed // //********************************************************************************************* bool CRio500::IORequest( RIO_DIRECTION Dir, // [i] Direction of transfer, RIO_IN or RIO_OUT RIO_REQUEST Req, // [i] Request command from RIO_REQUEST enum WORD wValue, // [i] The nebulous "value" parameter... WORD wIndex, // [i] The nebulous "index" parameter... DWORD dwLen, // [i] Length of pData buffer void* pData) // [o] Result of IO transaction { DWORD dwBytesReturned = 0; // Diamond's custom structure for a USB request.. struct { BYTE byRequestType; BYTE byRequest; WORD wValue; WORD wIndex; WORD wLen; void* pData; } IOBlock; // Fill out the IO block.. IOBlock.byRequestType = (Dir == DIR_IN) ? 0xC0 : 0x40; IOBlock.byRequest = Req; IOBlock.wValue = wValue; IOBlock.wIndex = wIndex; IOBlock.wLen = (WORD)dwLen; IOBlock.pData = pData; // Have a chat with the device driver.. if (!DeviceIoControl(m_hRio500, CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS), &IOBlock, sizeof(IOBlock), pData, dwLen, &dwBytesReturned, NULL)) { // got an error .. we wait 50 msecs and retry Sleep(50); if (!DeviceIoControl(m_hRio500, CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS), &IOBlock, sizeof(IOBlock), pData, dwLen, &dwBytesReturned, NULL)) { throw CLocalException(RIO_E_COULDNT_OPEN_DEVICE, L"IORequest Error !"); return false; } } // We're happy... return true; } //********************************************************************************************* // Description: Build a text bitmap inside of a VARIANT. See BuildTextBitmap. The bitmap // is constructed inside of a 1536 element VT_UI1 SAFEARRAY and crammed into a // variant. Yum. // // Returns: true Everything worked. // false Nothing worked. // //********************************************************************************************* bool CRio500::BuildTextBitmapInVariant( BSTR bsText, // [i] Text to render to bitmap _variant_t& vDest, // [o] Destination variant for bitmap WORD& wBlocks) // [o] The number of 8-pixel blocks needed to display all of the text { bool bReturn = true; // Array of VT_UI1.. vDest.vt = VT_ARRAY|VT_UI1; // Stupid safe arrays... SAFEARRAYBOUND SAB; SAB.lLbound = 0; SAB.cElements = RIO_BITMAPSIZE; vDest.parray = SafeArrayCreate(VT_UI1, 1, &SAB); // Get access to the array's naughty bits... BYTE* pDest = NULL; SafeArrayAccessData(vDest.parray, (void**)&pDest); // Build the bitmap.. bReturn = BuildTextBitmap(bsText, pDest, wBlocks); // Let go of the naughty bits SafeArrayUnaccessData(vDest.parray); return bReturn; } //********************************************************************************************* // Description: Build a 768x16x1bpp bitmap from the specified text and the font properties // of the object. This bitmap will be scrolled on the Rio's display. This is a // KILLER feature of the Rio! Render it as k-RaD as you like and be 31337 (or // something...) // // Returns: true Bitmap successfully rendered // false Something went horribly awry. // //********************************************************************************************* bool CRio500::BuildTextBitmap( BSTR bsText, // [i] Text to render BYTE* pBitmapData, // [o] Pointer to 1536 bytes of data to be filled with bitmappy goodness WORD& wBlocks) // [o] The number of 8-pixel blocks required to render the text { bool bReturn = true; HBITMAP hBMP = NULL; HDC hDC = NULL; HBITMAP hOldBMP = NULL; int nTextLen = SysStringLen(bsText); HFONT hFont = NULL; HFONT hOldFont = NULL; BITMAPINFO* pBMI = NULL; OSVERSIONINFO OSVersion; bool bIsUnicode = false; char* szANSIFontName = NULL; unsigned short* szANSIFontNameWide = NULL; char* szANSIText = NULL; ATLTRACE("BuildTextBitmap: %S\n", bsText); try { ZeroMemory(&OSVersion, sizeof(OSVersion)); OSVersion.dwOSVersionInfoSize = sizeof(OSVersion); if (!GetVersionEx(&OSVersion)) throw false; if (OSVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) bIsUnicode = true; // Make a bitmap.. if ((hBMP = CreateBitmap(768, 16, 1, 1, NULL)) == NULL) throw false; // Create a DC to draw in the bitmap.. if ((hDC = CreateCompatibleDC(NULL)) == NULL) throw false; // Put the bitmap in the DC.. if ((hOldBMP = (HBITMAP)SelectObject(hDC, hBMP)) == NULL) throw false; RECT r; // The size of the bitmap.. SetRect(&r, 0, 0, 768, 16); // Make it all white.. if (!FillRect(hDC, &r, (HBRUSH)GetStockObject(WHITE_BRUSH))) throw false; // Calculate the font height.. int nHeight = -MulDiv(m_lFontSize, GetDeviceCaps(hDC, LOGPIXELSY), 72); // Make the font.. if (bIsUnicode) hFont = CreateFontW(nHeight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, m_bsFontName); else { // For evil Windows 98, we can't use the warm and fuzzy Unicode version of // CreateFont, so we have to convert the BSTR to an MBCS (really SBCS) string // and then call the ANSI version. Blech. //int iBufSize; //iBufSize = WideCharToMultiByte(CP_ACP, 0, m_bsFontName, // SysStringLen(m_bsFontName), NULL, 0, NULL, NULL); // szANSIFontName = (char*)malloc(iBufSize+1); //ZeroMemory(szANSIFontName, iBufSize+1); //iBufSize = WideCharToMultiByte(CP_ACP, 0, m_bsFontName, // SysStringLen(m_bsFontName), szANSIFontName, iBufSize, NULL, NULL); //DWORD myBytesWritten; //HANDLE myFile = CreateFile(_bstr_t("TEST.TXT"), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, // CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); szANSIFontName = (char*)malloc((m_bsFontName.length())+1); //szANSIFontNameWide = (char*)malloc((SysStringLen(m_bsFontName)*2)+1); szANSIFontNameWide = m_bsFontName.copy(); // WriteFile(myFile,szANSIFontNameWide,20,&myBytesWritten,NULL); int i = 0,j=0; for (;i< m_bsFontName.length();i+=1) { szANSIFontName[j] = szANSIFontNameWide[i]; j++; } szANSIFontName[j] = 0x0; //WriteFile(myFile,szANSIFontName,strlen(szANSIFontName),&myBytesWritten,NULL); //CloseHandle(myFile); hFont = CreateFontA(nHeight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, szANSIFontName); } // Did it work? if (hFont == NULL) throw false; // Put the font in the DC if ((hOldFont = (HFONT)SelectObject(hDC, hFont)) == NULL) throw false; SIZE TextExtent; // Get the extent of the text if (bIsUnicode) { if (!GetTextExtentPoint32W(hDC, bsText, nTextLen, &TextExtent)) throw false; } else { // For evil Windows 98, we can't use the warm and fuzzy Unicode version of // CreateFont, so we have to convert the BSTR to an MBCS (really SBCS) string // and then call the ANSI version. Blech. int iBufSize; iBufSize = WideCharToMultiByte(CP_ACP, 0, bsText, SysStringLen(bsText), NULL, 0, NULL, NULL); szANSIText = (char*)malloc(iBufSize+1); ZeroMemory(szANSIText, iBufSize+1); iBufSize = WideCharToMultiByte(CP_ACP, 0, bsText, SysStringLen(bsText), szANSIText, iBufSize, NULL, NULL); if (!GetTextExtentPoint32A(hDC, szANSIText, nTextLen, &TextExtent)) throw false; } // divide by 8 to get number of blocks.. wBlocks = TextExtent.cx / 8; // Add one if we have any extra bits.. if (TextExtent.cx % 8) wBlocks++; // Black text please.. SetTextColor(hDC, RGB(0x00, 0x00, 0x00)); // Draw the text if (bIsUnicode) { if (!TextOutW(hDC, 0, 0, bsText, nTextLen)) throw false; } else { if (!TextOutA(hDC, 0, 0, szANSIText, nTextLen)) throw false; } // Setup a temporary bitmap to get the DIB bits too.. BYTE TempBitmap[RIO_BITMAPSIZE]; DWORD dwBMISize = sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 1; // setup a nice new bitmap info struct.. pBMI = (BITMAPINFO*)malloc(dwBMISize); pBMI->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pBMI->bmiHeader.biHeight = -16; pBMI->bmiHeader.biWidth = 768; pBMI->bmiHeader.biPlanes = 1; pBMI->bmiHeader.biBitCount = 1; pBMI->bmiHeader.biCompression = BI_RGB; // Get DIB bits.. BOOL bOk = GetDIBits(hDC, hBMP, 0, 16, TempBitmap, pBMI, DIB_RGB_COLORS); _ASSERTE(bOk); // This gets a little weird... we have to transmorgify the bitmap into 8-pixel wide // blocks, instead of just giving it to the Rio straight.. While we're at it, we // invert the bits so it shows up properly on the Rio's display. For those of you // playing the home game, you're asking yourselves "Why didn't he just render white // text on a black background?" Because it didn't work.. for some reason. I always // just got a big black bitmap - which was pretty boring. I'm not sure what the deal // is, but because I A) have to transmorgify it anyway, it doesn't matter and B) didn't // want to screw with it a lot. 2 bonus points if you now the origin of "transmorgify" BYTE* p = pBitmapData; ZeroMemory(pBitmapData, RIO_BITMAPSIZE); // Transmorgify for (WORD b = 0; b < wBlocks; b++) { for (WORD y = 0; y < 16; y++, p++) *p = ~TempBitmap[(y*96)+b]; } } catch (bool e) { bReturn = e; } // Free ANSI version of font name (if need be) if (szANSIFontName) free(szANSIFontName); // Free ANSI version of text (if need be) if (szANSIText) free(szANSIText); // Free bitmap info struct if (pBMI) free(pBMI); // Free font if (hOldFont && hDC && hFont) { SelectObject(hDC, hOldFont); DeleteObject(hFont); } // Free bitmap if (hOldBMP && hDC && hBMP) { SelectObject(hDC, hOldBMP); DeleteObject(hBMP); } // Free DC if (hDC) DeleteDC(hDC); return bReturn; } //********************************************************************************************* // Description: Determine if there is a folder on the Rio already. Returns true if there is // *NOT* a folder already available. // // Returns: true No folders exist on the Rio // false There are folders already on the Rio // //********************************************************************************************* bool CRio500::IsFirstFolder(long lFlash) { DWORD dwResult; // Get the number of folders.. if ((dwResult = SendCommand(REQ_QUERY_NUM_FOLDER_BLOCKS, 0xFF00, lFlash)) > 0) return false; // Wait a bit. NOP(); NOP(); // Try again, just to make sure.. (for some reason) if ((dwResult = SendCommand(REQ_QUERY_NUM_FOLDER_BLOCKS, 0xFF00, lFlash)) > 0) return false; // Wait a bit.. NOP(); NOP(); // Okay, we're done. SendCommand(REQ_END_FOLDER_TRANSFERS, 0xFF00, lFlash); return true; } //********************************************************************************************* // Description: Read a list of folders into an STL collection. // // Returns: An STL collection containing all the folders on the Rio. // //********************************************************************************************* CRio500::FOLDERENTRYLIST CRio500::ReadFolderEntries(long lFlash) { FOLDERENTRYLIST Folders; BYTE* pBlock = NULL; DWORD dwTotalRead = 0; DWORD dwFolderCount = 0; DWORD dwComStatus = 0; SRioFolderEntry* pFE; // The mysterious read command... if (!SendReadCommand(0xFF00, 1, lFlash)) return Folders; // Allocate memory for a block of data.. BYTE* pFolderBlock = new BYTE[RIO_BLOCKSIZE]; // Read some data. dwTotalRead = BulkRead(pFolderBlock, RIO_BLOCKSIZE); // Make sure we got it okay. if (dwTotalRead != RIO_BLOCKSIZE) { delete pFolderBlock; return Folders; } // cast the pointer to somethign we can deal with.. pFE = (SRioFolderEntry*)pFolderBlock; // Loop through folders, adding them to the collection while (pFE->wOffset != 0xFFFF && dwFolderCount < 8) { Folders.push_back(*pFE); dwFolderCount++; pFE++; } // Free memory for folder block delete pFolderBlock; return Folders; } //********************************************************************************************* // Description: Build a structure the Rio can understand from the object that COM understands // (IFolder). // // Returns: true Conversion successful // false Could not build folder entry // //********************************************************************************************* bool CRio500::BuildFolderEntry( IFolder* pFolder, // [i] An IFolder object SRioFolderEntry& NewFolder) // [o] A folder entry structure the Rio understands.. { BSTR bsName = NULL; VARIANT vBitmap; bool bRetVal = true; // Prepare bitmap variant VariantInit(&vBitmap); try { // Zap the folder entry.. ZeroMemory(&NewFolder, sizeof(SRioFolderEntry)); // Set who-know what to 0x002100FF and the time. NewFolder.dwDunno3 = 0x002100FF; NewFolder.dwTime = time(NULL); // Get the folder name pFolder->get_Name(&bsName); // ASCII-ize it... if (!WideCharToMultiByte(CP_ACP, NULL, bsName, SysStringLen(bsName), NewFolder.szName1, 362, NULL, NULL)) { DWORD dwError = GetLastError(); throw false; } // Copy to secondary name.. strncat(NewFolder.szName2, NewFolder.szName1, 127); // Get bitmap and bitmap blocks.. pFolder->get_BitmapBlocks(&NewFolder.Bitmap.wNumBlocks); pFolder->get_Bitmap(&vBitmap); // Make sure have a valid VARIANT.. if (vBitmap.vt != (VT_ARRAY|VT_UI1)) throw false; BYTE* pBitmap = NULL; long lUBound = 0; // Make sure the SAFEARRAY contains enough information for us.. SafeArrayGetUBound(vBitmap.parray, 1, &lUBound); if (lUBound < 1535) throw false; // Get the SAFEARRAY's naughty bits.. SafeArrayAccessData(vBitmap.parray, (void**)&pBitmap); // copy... memcpy(NewFolder.Bitmap.Image, pBitmap, RIO_BITMAPSIZE); // Release the naughty bits. SafeArrayUnaccessData(vBitmap.parray); } catch (bool e) { bRetVal = e; } // Free folder name. if (bsName) SysFreeString(bsName); VariantClear(&vBitmap); return bRetVal; } //********************************************************************************************* // Description: Allocate an empty folder block for the specified number of folders.. // // Returns: true Empty folder written to Rio // false Could not write empty folder to Rio // //********************************************************************************************* bool CRio500::AllocateEmptyFolderBlock( DWORD dwNumFolders, long lFlash) // [i] Number of folders desired.. { DWORD dwAddress = dwNumFolders; BYTE* pEmptyBlock = new BYTE[RIO_BLOCKSIZE]; // Munge address. for some reason.. dwAddress <<= 8; dwAddress |= 0x000000FF; dwAddress &= 0x00000FFF; // "format" the empty block ClearBlock(pEmptyBlock); // Tell the Rio it's about to get something.. bool bRetVal = SendWriteCommand(dwAddress, 1, lFlash); if (bRetVal) { // Wait a bit.. NOP(); NOP(); // Blast out the empty folder block bRetVal = BulkWrite(pEmptyBlock, RIO_BLOCKSIZE) == RIO_BLOCKSIZE; } // Clean up after ourselves.. delete pEmptyBlock; return bRetVal; } //********************************************************************************************* // Description: "Format" a block for use by the Rio // // Returns: Nothing // //********************************************************************************************* void CRio500::ClearBlock( BYTE *pBlock) // [i] Block to format.. { BYTE* p; // Bzzzt.. ZeroMemory(pBlock, RIO_BLOCKSIZE); // Initialize folder entries to signal "no folder here".. p = pBlock; if (pBlock) { while ((p - pBlock) < RIO_BLOCKSIZE) { *p = 0xFF; *(p+1) = 0xFF; p += sizeof(SRioFolderEntry); } } } //********************************************************************************************* // Description: Tell the Rio it's about to get data on the bulk write pipe.. // // Returns: true Okay - blast away... // false Nope - something's wrong. // //********************************************************************************************* bool CRio500::SendWriteCommand( DWORD dwAddress, // [i] Address to write to DWORD dwNumBlocks, long lFlash) // [i] Number of blocks to write { // Calculate length we'll be sending.. DWORD dwLength = dwNumBlocks * RIO_BLOCKSIZE; WORD wWriteBlocks = WORD(dwLength / RIO_BIGBLOCKSIZE); WORD wWriteRemainder = WORD(dwLength % RIO_BIGBLOCKSIZE); DWORD dwWriteStatus = 0; // Give Rio the address we'd like to write to.. dwWriteStatus = SendCommand(REQ_SET_WRITE_ADDRESS, dwAddress, lFlash); // Who knows... if (!(dwWriteStatus = SendCommand(REQ_MYSTERY, 0xFFFF, lFlash))) return false; // Tell Rio to open wide... if (!(dwWriteStatus = SendCommand(REQ_WRITE_TO_USB, wWriteBlocks, wWriteRemainder))) return false; return true; } //********************************************************************************************* // Description: Write a chunk of data to bulk write pipe. // // Returns: Number of bytes written // //********************************************************************************************* DWORD CRio500::BulkWrite( void* pBlock, DWORD dwNumBytes) { BYTE* p = (BYTE*)pBlock; DWORD dwCount = 0; DWORD dwBytesLeft = dwNumBytes; DWORD dwBytesWritten = 0; do { BOOL bRet; // Blast away... bRet = WriteFile(m_hRio500Write, p, dwBytesLeft, &dwCount, NULL); _ASSERTE(bRet); // Make it worked, and calculate how much more to write.. if (dwCount > 0) { p += dwCount; dwBytesWritten += dwCount; dwBytesLeft -= dwCount; } } while (dwBytesLeft > 0 && dwCount > 0); // Return number of bytes written return dwBytesWritten; } //********************************************************************************************* // Description: Write folder entries to the Rio // // Returns: true Folders written successfully // false Could not write folders // //********************************************************************************************* bool CRio500::WriteFolderEntries( CRio500::FOLDERENTRYLIST& Folders, long lFlash) // [i] An STL collection of folders to write.. { // Do some calculations up front to figure the number of blocks we'll need.. DWORD dwListLen = Folders.size(); DWORD dwNumBlocks = dwListLen >> 3; DWORD dwNumFolders = 0; // Check for remainder.. if (dwListLen & 0x00000007) dwNumBlocks++; // Tell Rio we're going to write to it.. SendWriteCommand(0xFF00, dwNumBlocks, lFlash); // Wait a bit.. NOP(); NOP(); // Allocate temporary buffer BYTE* pFolderBlock = new BYTE[RIO_BLOCKSIZE]; SRioFolderEntry* pFE = (SRioFolderEntry*)pFolderBlock; // Clear folder block.. ClearBlock(pFolderBlock); // Copy data from collection into block for (FOLDERENTRYLIST::iterator it = Folders.begin(); it != Folders.end() && dwNumFolders < 8; it++, dwNumFolders++, pFE++) memcpy(pFE, it, sizeof(SRioFolderEntry)); // Write the block bool bRetVal = BulkWrite(pFolderBlock, RIO_BLOCKSIZE) == RIO_BLOCKSIZE; // Delete temporary data delete pFolderBlock; return bRetVal; } //********************************************************************************************* // Description: Weirdness to tell the Rio where folders have been written to. // // Returns: true Okay, location accepted. // false IORequest failed.. // //********************************************************************************************* bool CRio500::SendFolderLocation( DWORD dwOffset, // [i] Offset to folder block DWORD dwFolderNum, long lFlash) // [i] Number of folders { SRioFolderLocation Location; // Bzzt.. ZeroMemory(&Location, sizeof(SRioFolderLocation)); // Setup structure.. Location.wOffset = (WORD)dwOffset; Location.wBytes = (WORD)RIO_BLOCKSIZE; Location.wFolderNum = (WORD)dwFolderNum; // The only DIR_OUT IO request we need.. return IORequest(DIR_OUT, REQ_SEND_FOLDER_LOCATION, 0x0000, lFlash, sizeof(SRioFolderLocation), &Location); } //********************************************************************************************* // Description: Tell the Rio we'd like to read some data from it. // // Returns: true Okay - go ahead and read // false Call failed.. // //********************************************************************************************* bool CRio500::SendReadCommand( DWORD dwAddress, // [i] Address to read from.. DWORD dwNumBlocks, long lFlash) // [i] Number of blocks to read { // Figure how much data we want.. DWORD dwLength = dwNumBlocks * RIO_BLOCKSIZE; WORD wReadBlocks = WORD(dwLength / RIO_BIGBLOCKSIZE); WORD wReadRemainder = WORD(dwLength % RIO_BIGBLOCKSIZE); DWORD dwReadStatus = 0; // Setup Rio for reading... if (!(dwReadStatus = SendCommand(REQ_QUERY_NUM_FOLDER_BLOCKS, (WORD)dwAddress, lFlash))) return false; // Setup Rio for reading... if (!(dwReadStatus = SendCommand(REQ_QUERY_FOLDER_BLOCK, (WORD)dwAddress, lFlash))) return false; // Tell Rio how much data we're going to read in 64k chunks and the remainder.. if (!(dwReadStatus = SendCommand(REQ_READ_FROM_USB, wReadBlocks, wReadRemainder))) return false; return true; } //********************************************************************************************* // Description: Perform a bulk read on the USB bulk read pipe. // // Returns: The number of bytes read // //********************************************************************************************* DWORD CRio500::BulkRead( void* pBlock, // [o] Buffer to receive data DWORD dwNumBytes) // [i] Size of buffer { DWORD dwTotalRead = 0; DWORD dwCount = 0; DWORD dwBytesLeft = dwNumBytes; BYTE* p = (BYTE*)pBlock; do { // Do the read - make sure it worked.. if (!(ReadFile(m_hRio500Read, p, dwBytesLeft, &dwCount, NULL))) { DWORD dwError = GetLastError(); _ASSERTE(FALSE); } // Figure out if we need to read more.. if (dwCount > 0) { dwTotalRead += dwCount; dwBytesLeft -= dwCount; p += dwCount; } // Read more if necessary.. } while (dwTotalRead < dwNumBytes && dwCount > 0); // Return total bytes read return dwTotalRead; } //********************************************************************************************* // Description: Read song entries from the Rio into an STL collection of Rio-friendly structs // // Returns: An STL collection of song entries on the Rio. // //********************************************************************************************* CRio500::SONGENTRYLIST CRio500::ReadSongEntries( CRio500::SRioFolderEntry& Folder, // [i] Folder entry to read songs from DWORD dwFolderNum, long lFlash) // [i] Folder number to read { // Calculate number of blocks we'll need.. SONGENTRYLIST Songs; DWORD dwNumBlocks = Folder.wFirstFreeEntryOfs / RIO_BLOCKSIZE; DWORD dwCount = 8 * dwNumBlocks; // Check remainder.. if ((Folder.wFirstFreeEntryOfs % RIO_BLOCKSIZE) > 0) dwNumBlocks++; // Add in remainder.. dwCount += (Folder.wFirstFreeEntryOfs % RIO_BLOCKSIZE) / sizeof(SRioFolderEntry); // No blocks? No songs! if (dwNumBlocks == 0) return Songs; // Calculate folder address.. DWORD dwAddress = dwFolderNum; dwAddress <<= 8; dwAddress |= 0x000000FF; dwAddress &= 0x00000FFF; // Setup buffers for reading song entry blocks.. DWORD dwSize = dwNumBlocks * RIO_BLOCKSIZE; BYTE* pSongBlock = new BYTE[dwSize]; // Tell Rio we're going to read.. SendReadCommand(dwAddress, dwNumBlocks, lFlash); // Do the read.. DWORD dwTotalRead = BulkRead(pSongBlock, dwSize); // If we didn't get what we expect, stop. if (dwTotalRead != dwSize) { delete pSongBlock; return Songs; } // Step through song entries, adding them to the collection.. SRioSongEntry* pSE = (SRioSongEntry*)pSongBlock; while (dwCount && pSE->wOffset != 0xFFFF) { Songs.push_back(*pSE); pSE++; dwCount--; } // Clean up.. delete pSongBlock; return Songs; } //********************************************************************************************* // Description: Write song entries to the Rio. // // Returns: true The song entries were written successfully // false Could not write song entries // //********************************************************************************************* bool CRio500::WriteSongEntries( DWORD dwFolderNum, // [i] Folder number to write song entries to. CRio500::SONGENTRYLIST& Songs, long lFlash) { DWORD dwAddress = dwFolderNum; BYTE* pBlock = NULL; bool bRetVal = true; try { // Munge address.. dwAddress <<= 8; dwAddress |= 0x000000FF; dwAddress &= 0x00000FFF; // Calculate number of blocks we'll need.. DWORD dwNumBlocks = Songs.size() >> 3; // Handle remainder.. if (Songs.size() & 0x7) dwNumBlocks++; // Tell Rio we need to write to it... SendWriteCommand(dwAddress, dwNumBlocks, lFlash); // Allocate temporary block and clear it. pBlock = new BYTE[RIO_BLOCKSIZE]; ClearBlock(pBlock); SRioSongEntry* pSE = (SRioSongEntry*)pBlock; DWORD dwCount = 0; // Walk STL collection writing song entries.. for (SONGENTRYLIST::iterator it = Songs.begin(); it != Songs.end(); it++) { // Copy song entry to block.. memcpy(pSE, it, sizeof(SRioSongEntry)); dwCount++; pSE++; // Only 8 entries per block.. if (dwCount == 8) { // Write out full song entry block if (BulkWrite(pBlock, RIO_BLOCKSIZE) != RIO_BLOCKSIZE) throw false; // Bzzt.. dwCount = 0; ClearBlock(pBlock); pSE = (SRioSongEntry*)pBlock; } } // Write remaining song entry blocks.. if (dwCount) { if (BulkWrite(pBlock, RIO_BLOCKSIZE) != RIO_BLOCKSIZE) throw false; } } catch (bool e) { bRetVal = e; } // Clean up if (pBlock) delete pBlock; return bRetVal; } //********************************************************************************************* // Description: Searchs the file for the Header which contains info on bit rate // stereo and the like. Used by RIO500 to display proper time of a song // // Returns: File header info, or zero fail // //********************************************************************************************* DWORD CRio500::GetFrameHeader(HANDLE hFile) { DWORD fh = 0; int i; while (MP3ReadWord(&fh, hFile)) { while (fh == STR_RIFF) { // read 2 words if (!MP3ReadWord(&fh, hFile)) return 0; if (!MP3ReadWord(&fh, hFile)) return 0; if (fh != STR_MPEG) { if (fh != STR_WAVE) break; if (!MP3ReadWord(&fh, hFile)) return 0; if (fh != STR_fmt) break; if (!MP3ReadWord(&fh, hFile)) return 0; if (fh > 4096) break; if(!MP3ReadBlock(fh, hFile)) return 0; } if (!MP3ReadWord(&fh, hFile)) return 0; if (fh == STR_fact) { if (!MP3ReadWord(&fh, hFile)) return 0; if (fh > 4096) break; if(!MP3ReadBlock(fh, hFile)) return 0; if (!MP3ReadWord(&fh, hFile)) return 0; } break; } if (!SearchForFrameHeader(&fh,hFile)) return 0; if (fh != STR_RIFF) return (ntohl(fh)); // swap byte order } return 0; } //********************************************************************************************* // Description: Searchs a big block for Header // // Returns: true Found RIFF or header // false reached end of file, or failed // //********************************************************************************************* int CRio500::SearchForFrameHeader (DWORD *fhp, HANDLE hFile) { int i; i = 65536; // Don't ask me why this number is used while (!IsFrameHeader(*fhp)&& (*fhp != STR_RIFF)) { if (i-- == 0 || !MP3ReadByte(fhp, hFile)) return FALSE; // return false, fail } return TRUE; // Got a frame header or RIFF } //********************************************************************************************* // Description: Checks a given word to see if its a valid frame header. // // Returns: true if its a vaild frame header for MP3 // false not valid // //****************************************************************************************** DWORD CRio500::IsFrameHeader(DWORD fh) { return (((fh & 0xffe00000) == 0xffe00000) && // FFE is part of sync byte (((fh >> 17) & 3) != 0) && // layer descripiton 00 = reserved (((fh >> 12) & 0xf) != 0xf) && // bit rate FFFF = bad (((fh >> 10) & 0x3) != 0x3) && // sampling rate freq = reserved ((fh & 0xffff0000) != 0xfffe0000)); // Sync byte is FFFE, last bit is MP2 indicator } //********************************************************************************************* // Description: Gets a bunch of data from the MP3 File // // Returns: true read all the bytes requested // false Could not read all the requested bytes // //********************************************************************************************* bool CRio500::MP3ReadBlock (DWORD fh, HANDLE hFile) { DWORD dwNumRead; BYTE* pBlock; int i; pBlock = new BYTE[fh]; ReadFile(hFile, pBlock, fh, &dwNumRead, NULL); // Free buffer block if (pBlock) delete pBlock; if (dwNumRead!=fh) return FALSE; return TRUE; } //********************************************************************************************* // Description: Gets a full DWORD from the MP3 file // // Returns: true read 4 bytes // false Could not read 4 bytes // //********************************************************************************************* bool CRio500::MP3ReadWord(DWORD *fhp, HANDLE hFile) { DWORD dwNumRead; ReadFile(hFile, fhp, 4, &dwNumRead, NULL); *fhp=ntohl(*fhp); if (dwNumRead!=4) return FALSE; return TRUE; } //********************************************************************************************* // Description: Reads a byte from MP3 file and puts in into lower byte of a WORD. // // Returns: true read a byte // false Could not read a byte // //********************************************************************************************* bool CRio500::MP3ReadByte(DWORD *fhp, HANDLE hFile) { DWORD dwNumRead; BYTE n; ReadFile(hFile, &n, 1, &dwNumRead, NULL); if (!dwNumRead) return FALSE; // so we get a byte of data and insert it at the low byte *fhp = (*fhp << 8) | n; return TRUE; }