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