1 /*
2 _________ __ __
3 / _____// |_____________ _/ |______ ____ __ __ ______
4 \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
5 / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
6 /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
7 \/ \/ \//_____/ \/
8 ______________________ ______________________
9 T H E W A R B E G I N S
10 Stratagus - A free fantasy real time strategy game engine
11
12 rip_music_win32.c - rip audio CD on Windows with cdda2wav.exe
13 Copyright (C) 2011 aqrit and Pali Rohár <pali.rohar@gmail.com>
14
15 This program is free software: you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation, either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28 */
29
30 #include <windows.h>
31 #include <process.h>
32 #include <errno.h>
33 #include <stdio.h>
34
35 #include "rip_music.h"
36
37 #if _MSC_VER
38 #define snprintf _snprintf
39 #endif
40
41
42 #ifndef _NTDDSCSIH_
43 #include <ctype.h>
44 #define IOCTL_SCSI_GET_ADDRESS 0x00041018
45 #define IOCTL_SCSI_GET_INQUIRY_DATA 0x0004100C
46
47 typedef struct _SCSI_ADDRESS {
48 ULONG Length;
49 UCHAR PortNumber;
50 UCHAR PathId;
51 UCHAR TargetId;
52 UCHAR Lun;
53 } SCSI_ADDRESS, *PSCSI_ADDRESS;
54
55 typedef struct _SCSI_BUS_DATA {
56 UCHAR NumberOfLogicalUnits;
57 UCHAR InitiatorBusId;
58 ULONG InquiryDataOffset;
59 } SCSI_BUS_DATA, *PSCSI_BUS_DATA;
60
61 typedef struct _SCSI_ADAPTER_BUS_INFO {
62 UCHAR NumberOfBuses;
63 SCSI_BUS_DATA BusData[1];
64 } SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
65 #endif
66
67 #define SCSI_INFO_BUFFER_SIZE 2048 // just throw a huge buffer at it
68
GetSCSIAddressFromDriveLetter(const char drive_letter,PSCSI_ADDRESS scsi_address)69 int GetSCSIAddressFromDriveLetter(const char drive_letter, PSCSI_ADDRESS scsi_address) {
70
71 char file_name[8];
72 int result = 0;
73 HANDLE device_handle;
74
75 wsprintf(file_name, "\\\\.\\%c:", drive_letter);
76 device_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
77
78 if ( device_handle != INVALID_HANDLE_VALUE ) {
79
80 scsi_address->Length = sizeof(SCSI_ADDRESS);
81 scsi_address->PortNumber = 0;
82 scsi_address->PathId = 0;
83 scsi_address->TargetId = 0;
84 scsi_address->Lun = 0;
85 long bytes_returned;
86
87 if ( DeviceIoControl(device_handle, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_address, sizeof(SCSI_ADDRESS), (LPDWORD)&bytes_returned, FALSE) ) {
88
89 if ( bytes_returned == sizeof(SCSI_ADDRESS) )
90 result = 1;
91
92 } else {
93
94 if ( GetLastError() == 50 ) { // USB/FIREWIRE devices?
95
96 // as per cdda2wav
97 int temp = (int)(toupper(drive_letter) - 'A');
98 scsi_address->PortNumber = temp+64;
99 scsi_address->PathId = temp;
100 result = 1;
101
102 }
103
104 }
105
106 CloseHandle(device_handle);
107
108 }
109
110 return result;
111 }
112
113
GetSPTIAddressFromDriveLetter(const char drive_letter,char * spti_address)114 int GetSPTIAddressFromDriveLetter(const char drive_letter, char * spti_address) {
115
116 SCSI_ADDRESS target_scsi_address;
117
118 if ( ! GetSCSIAddressFromDriveLetter(drive_letter, &target_scsi_address) ) {
119
120 printf("Error: Cannot get SCSI address of drive %c\n", drive_letter);
121 return 1;
122
123 }
124
125 const int max_list = 128; // hopefully enough
126 short list[max_list];
127 int num_list = 0;
128 int i;
129
130 // add drives to the list
131 for ( i = 0; i < 26; i++ ) {
132
133 SCSI_ADDRESS scsi_address;
134
135 if ( GetSCSIAddressFromDriveLetter((char)('A'+i), &scsi_address) ) {
136
137 int list_index;
138 short value = ( scsi_address.PortNumber << 8 ) | scsi_address.PathId;
139
140 if ( num_list >= max_list )
141 break; // list is full
142
143 for ( list_index = 0; list_index < num_list; list_index++)
144 if ( list[list_index] == value )
145 break; // no duplicates
146
147 if ( list_index >= num_list )
148 list[num_list++] = value; // append to list
149
150 }
151
152 }
153
154 // add scsi devices to the list
155 for ( i = 0; ; i++ ) {
156
157 static char inquiry_buffer[SCSI_INFO_BUFFER_SIZE];
158 long bytes_returned;
159 char file_name[24];
160 HANDLE device_handle;
161 int bus;
162
163 wsprintf(file_name, "\\\\.\\SCSI%u:", i);
164 device_handle = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
165
166 if ( device_handle == INVALID_HANDLE_VALUE )
167 break;
168
169 if ( DeviceIoControl(device_handle, IOCTL_SCSI_GET_INQUIRY_DATA, NULL, 0, inquiry_buffer, SCSI_INFO_BUFFER_SIZE, (LPDWORD)&bytes_returned, NULL) ) {
170
171 for ( bus = 0; bus < ((PSCSI_ADAPTER_BUS_INFO)inquiry_buffer)->NumberOfBuses; bus++ ) {
172
173 int list_index;
174 short value = ( i << 8 ) | bus;
175
176 if ( num_list >= max_list )
177 break; // list is full
178
179 for ( list_index = 0; list_index < num_list; list_index++ )
180 if ( list[list_index] == value )
181 break; // no duplicates
182
183 if ( list_index >= num_list )
184 list[num_list++] = value; // append to list
185
186 }
187
188 }
189
190 CloseHandle(device_handle);
191
192 }
193
194 // find what would be the cdda2wav "bus"
195 short value = ( target_scsi_address.PortNumber << 8 ) | target_scsi_address.PathId;
196 int count = 0;
197 int found = 0;
198
199 for ( i = 0; i < num_list; i++ ) {
200
201 if ( list[i] < value )
202 count++;
203 else if ( list[i] == value )
204 found = 1;
205
206 }
207
208 if ( ! found || count >= 26 ) { // cdda2wav caps at 26
209
210 fprintf(stderr, "Unknown Error\n");
211 return 1;
212
213 }
214
215 sprintf(spti_address, "SPTI:%u,%u,%u", count, target_scsi_address.TargetId, target_scsi_address.Lun);
216 return 0;
217
218 }
219
GetDriveLetterFromPath(const char * path)220 char GetDriveLetterFromPath(const char * path) {
221
222 char save_cwd[_MAX_PATH];
223 char cwd[_MAX_PATH];
224 int res;
225
226 if ( ! GetCurrentDirectory(sizeof(save_cwd), save_cwd) ) {
227
228 fprintf(stderr, "Error: Cannot store working directory: %s\n", strerror(errno));
229 return 0;
230
231 }
232
233 if ( ! SetCurrentDirectory(path) ) {
234
235 fprintf(stderr, "Error: Cannot change directory to %s: %s\n", path, strerror(errno));
236 return 0;
237
238 }
239
240 res = GetCurrentDirectory(sizeof(cwd), cwd);
241
242 if ( ! res )
243 fprintf(stderr, "Error: Cannot get working directory: %s\n", strerror(errno));
244
245 if ( ! SetCurrentDirectory(save_cwd) )
246 fprintf(stderr, "Error: Cannot restore working directory: %s\n", strerror(errno));
247
248 if ( ! res )
249 return 0;
250 else
251 return cwd[0];
252
253 }
254
RipMusic(int expansion_cd,const char * data_dir,const char * dest_dir)255 int RipMusic(int expansion_cd, const char * data_dir, const char * dest_dir) {
256
257 const char cdda2wav[] = "cdda2wav.exe";
258 const char * args[7] = { cdda2wav, "-D", "", "-J", NULL, NULL, NULL };
259 char drive;
260 char spti[20];
261 int count = 0;
262 int i;
263
264 if ( ! ( drive = GetDriveLetterFromPath(data_dir) ) ) {
265
266 fprintf(stderr, "Error: Cannot get drive letter of path %s\n", data_dir);
267 return 1;
268
269 }
270
271 if ( GetSPTIAddressFromDriveLetter(drive, spti) != 0 ) {
272
273 fprintf(stderr, "Error: Cannot get SPTI address of drive %c\n", drive);
274 return 1;
275
276 }
277
278 printf("Found CD-ROM device: %s\n", spti);
279 fflush(stdout);
280
281 args[2] = spti;
282
283 if ( _spawnvp(_P_WAIT, cdda2wav, args) != 0 )
284 return 1;
285
286 for ( i = 0; MusicNames[i]; ++i ) {
287
288 char num[3];
289 char file[_MAX_PATH];
290
291 if ( ! expansion_cd && ! MusicNames[i+1] )
292 break;
293
294 snprintf(num, sizeof(num), "%d", i+2);
295 snprintf(file, sizeof(file), "\"%s/%s.wav\"", dest_dir, MusicNames[i]);
296
297 args[3] = "-t";
298 args[4] = num;
299 args[5] = file;
300
301 if ( _spawnvp(_P_WAIT, cdda2wav, args) == 0 )
302 ++count;
303
304 }
305
306 if ( count == 0 )
307 return 1;
308 else
309 return 0;
310
311 }
312