1 /* 2 * PROJECT: ReactOS Spooler API 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Functions giving information about DEVMODE structures 5 * COPYRIGHT: Copyright 2016-2017 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 typedef struct _MINIMUM_SIZE_TABLE 11 { 12 DWORD dwField; 13 WORD wSize; 14 } 15 MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE; 16 17 /** 18 * Minimum required DEVMODEA structure size based on the used fields. Must be in descending order! 19 */ 20 static MINIMUM_SIZE_TABLE MinimumSizeA[] = { 21 { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) }, 22 { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) }, 23 { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) }, 24 { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) }, 25 { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) }, 26 { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) }, 27 { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) }, 28 { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) }, 29 { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) }, 30 { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) }, 31 { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) }, 32 { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) }, 33 { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) }, 34 { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) }, 35 { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) }, 36 { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) }, 37 { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) }, 38 { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) }, 39 { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) }, 40 { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) }, 41 { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) }, 42 { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) }, 43 { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) }, 44 { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) }, 45 { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) }, 46 { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) }, 47 { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) }, 48 { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) }, 49 { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) }, 50 { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) }, 51 { 0, 0 } 52 }; 53 54 /** 55 * Minimum required DEVMODEW structure size based on the used fields. Must be in descending order! 56 */ 57 static MINIMUM_SIZE_TABLE MinimumSizeW[] = { 58 { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) }, 59 { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) }, 60 { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) }, 61 { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) }, 62 { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) }, 63 { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) }, 64 { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) }, 65 { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) }, 66 { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) }, 67 { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) }, 68 { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) }, 69 { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) }, 70 { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) }, 71 { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) }, 72 { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) }, 73 { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) }, 74 { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) }, 75 { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) }, 76 { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) }, 77 { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) }, 78 { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) }, 79 { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) }, 80 { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) }, 81 { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) }, 82 { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) }, 83 { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) }, 84 { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) }, 85 { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) }, 86 { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) }, 87 { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) }, 88 { 0, 0 } 89 }; 90 91 /** 92 * Replace the last character by a null terminator if the given ANSI string is not null-terminated. 93 */ 94 static __inline void 95 _FixStringA(PBYTE String, DWORD cbString) 96 { 97 const PBYTE pLastCharacter = &String[cbString / sizeof(BYTE) - 1]; 98 PBYTE p = String; 99 100 while (*p) 101 { 102 if (p == pLastCharacter) 103 { 104 *p = 0; 105 break; 106 } 107 108 p++; 109 } 110 } 111 112 /** 113 * Replace the last character by a null terminator if the given Unicode string is not null-terminated. 114 */ 115 static __inline void 116 _FixStringW(PWSTR String, DWORD cbString) 117 { 118 const PWSTR pLastCharacter = &String[cbString / sizeof(WCHAR) - 1]; 119 PWSTR p = String; 120 121 while (*p) 122 { 123 if (p == pLastCharacter) 124 { 125 *p = 0; 126 break; 127 } 128 129 p++; 130 } 131 } 132 133 BOOL WINAPI 134 IsValidDevmodeA(PDEVMODEA pDevmode, size_t DevmodeSize) 135 { 136 PMINIMUM_SIZE_TABLE pTable = MinimumSizeA; 137 WORD wRequiredSize; 138 139 TRACE("IsValidDevmodeA(%p, %lu)\n", pDevmode, DevmodeSize); 140 141 // Check if a Devmode was given at all. 142 if (!pDevmode) 143 goto Failure; 144 145 // Verify that DevmodeSize is large enough to hold the public and private members of the structure. 146 if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra) 147 goto Failure; 148 149 // If the structure has private members, the public structure must be 32-bit packed. 150 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4) 151 goto Failure; 152 153 // Now determine the minimum possible dmSize based on the given fields in dmFields. 154 wRequiredSize = FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields); 155 156 while (pTable->dwField) 157 { 158 if (pDevmode->dmFields & pTable->dwField) 159 { 160 wRequiredSize = pTable->wSize; 161 break; 162 } 163 164 pTable++; 165 } 166 167 // Verify that the value in dmSize is big enough for the used fields. 168 if (pDevmode->dmSize < wRequiredSize) 169 goto Failure; 170 171 // Check if dmDeviceName and (if used) dmFormName are null-terminated. 172 // Fix this if they aren't. 173 _FixStringA(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName)); 174 if (pDevmode->dmFields & DM_FORMNAME) 175 _FixStringA(pDevmode->dmFormName, sizeof(pDevmode->dmFormName)); 176 177 // Return success without setting the error code. 178 return TRUE; 179 180 Failure: 181 SetLastError(ERROR_INVALID_DATA); 182 return FALSE; 183 } 184 185 BOOL WINAPI 186 IsValidDevmodeW(PDEVMODEW pDevmode, size_t DevmodeSize) 187 { 188 PMINIMUM_SIZE_TABLE pTable = MinimumSizeW; 189 WORD wRequiredSize; 190 191 TRACE("IsValidDevmodeW(%p, %lu)\n", pDevmode, DevmodeSize); 192 193 // Check if a Devmode was given at all. 194 if (!pDevmode) 195 goto Failure; 196 197 // Verify that DevmodeSize is large enough to hold the public and private members of the structure. 198 if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra) 199 goto Failure; 200 201 // If the structure has private members, the public structure must be 32-bit packed. 202 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4) 203 goto Failure; 204 205 // Now determine the minimum possible dmSize based on the given fields in dmFields. 206 wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields); 207 208 while (pTable->dwField) 209 { 210 if (pDevmode->dmFields & pTable->dwField) 211 { 212 wRequiredSize = pTable->wSize; 213 break; 214 } 215 216 pTable++; 217 } 218 219 // Verify that the value in dmSize is big enough for the used fields. 220 if (pDevmode->dmSize < wRequiredSize) 221 goto Failure; 222 223 // Check if dmDeviceName and (if used) dmFormName are null-terminated. 224 // Fix this if they aren't. 225 _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName)); 226 if (pDevmode->dmFields & DM_FORMNAME) 227 _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName)); 228 229 // Return success without setting the error code. 230 return TRUE; 231 232 Failure: 233 SetLastError(ERROR_INVALID_DATA); 234 return FALSE; 235 } 236 237 BOOL WINAPI 238 IsValidDevmodeNoSizeW(PDEVMODEW pDevmode) 239 { 240 PMINIMUM_SIZE_TABLE pTable = MinimumSizeW; 241 WORD wRequiredSize; 242 243 TRACE("IsValidDevmodeNoSizeW(%p)\n", pDevmode); 244 245 // Check if a Devmode was given at all. 246 if (!pDevmode) 247 goto Failure; 248 249 // If the structure has private members, the public structure must be 32-bit packed. 250 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4) 251 goto Failure; 252 253 // Now determine the minimum possible dmSize based on the given fields in dmFields. 254 wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields); 255 256 while (pTable->dwField) 257 { 258 if (pDevmode->dmFields & pTable->dwField) 259 { 260 wRequiredSize = pTable->wSize; 261 break; 262 } 263 264 pTable++; 265 } 266 267 // Verify that the value in dmSize is big enough for the used fields. 268 if (pDevmode->dmSize < wRequiredSize) 269 goto Failure; 270 271 // Check if dmDeviceName and (if used) dmFormName are null-terminated. 272 // Fix this if they aren't. 273 _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName)); 274 if (pDevmode->dmFields & DM_FORMNAME) 275 _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName)); 276 277 // Return success without setting the error code. 278 return TRUE; 279 280 Failure: 281 SetLastError(ERROR_INVALID_DATA); 282 return FALSE; 283 } 284 285 void RosConvertAnsiDevModeToUnicodeDevmode(PDEVMODEA pDevModeInput, PDEVMODEW *pDevModeOutput) 286 { 287 // FIXME: This function should become ConvertAnsiDevModeToUnicodeDevmode when its parameters are known! 288 289 // Check if a pDevModeInput and pDevModeOutput are both not NULL. 290 if (!pDevModeInput || !pDevModeOutput) 291 return; 292 293 *pDevModeOutput = GdiConvertToDevmodeW(pDevModeInput); 294 } 295 296 // Internal counterpart to GdiConvertToDevmodeW from gdi32 297 static __inline DEVMODEA* 298 _ConvertToDevmodeA(const DEVMODEW *dmW) 299 { 300 DEVMODEA *dmA; 301 WORD dmA_size, dmW_size; 302 size_t BytesToCopy; 303 304 dmW_size = dmW->dmSize; 305 306 /* this is the minimal dmSize that XP accepts */ 307 if (dmW_size < FIELD_OFFSET(DEVMODEW, dmFields)) 308 return NULL; 309 310 // Guard against callers that set dmSize incorrectly. 311 if (dmW_size > sizeof(DEVMODEW)) 312 dmW_size = sizeof(DEVMODEW); 313 314 // dmA_size must become dmW_size without the additional 1 byte per character for each Unicode string (dmDeviceName and dmFormName). 315 dmA_size = dmW_size - CCHDEVICENAME; 316 if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR)) 317 dmA_size -= CCHFORMNAME; 318 319 // Allocate the required bytes, that is dmSize for the ANSI DEVMODEA structure plus any extra bytes requested through dmDriverExtra. 320 dmA = HeapAlloc(GetProcessHeap(), 0, dmA_size + dmW->dmDriverExtra); 321 if (!dmA) return NULL; 322 323 // Every valid DEVMODEW has a dmDeviceName, which we convert to ANSI here. 324 WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1, (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL); 325 326 // Copy everything up to dmFormName or the remaining dmW_size, whatever is smaller. 327 BytesToCopy = min(FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion), dmW_size - CCHDEVICENAME * sizeof(WCHAR)); 328 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion, BytesToCopy); 329 330 // Handle dmFormName if the input DEVMODEW is large enough to contain one. 331 if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR)) 332 { 333 if (dmW->dmFields & DM_FORMNAME) 334 WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1, (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL); 335 else 336 dmA->dmFormName[0] = 0; 337 338 // Copy the remaining fields. 339 if (dmW_size > FIELD_OFFSET(DEVMODEW, dmLogPixels)) 340 memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW_size - FIELD_OFFSET(DEVMODEW, dmLogPixels)); 341 } 342 343 // Append dmDriverExtra if required. 344 if (dmW->dmDriverExtra) 345 memcpy((char *)dmA + dmA_size, (const char *)dmW + dmW_size, dmW->dmDriverExtra); 346 347 // Set the corrected dmSize and we are done. 348 dmA->dmSize = dmA_size; 349 350 return dmA; 351 } 352 353 void RosConvertUnicodeDevModeToAnsiDevmode(PDEVMODEW pDevModeInput, PDEVMODEA pDevModeOutput) 354 { 355 PDEVMODEA pTmp; 356 357 // FIXME: This function should become ConvertUnicodeDevModeToAnsiDevmode when its parameters are known! 358 359 // Check if a pDevModeInput and pDevModeOutput are both not NULL. 360 if (!pDevModeInput || !pDevModeOutput) 361 return; 362 363 pTmp = _ConvertToDevmodeA(pDevModeInput); 364 memcpy( pDevModeOutput, pTmp, pTmp->dmSize + pTmp->dmDriverExtra); // Copy into a Wide char (Larger) buffer. 365 HeapFree(hProcessHeap, 0, pTmp); 366 } 367