From 62b0d7ae502b96ae3d89941a495694728dd6969f Mon Sep 17 00:00:00 2001 From: AShiningRay Date: Fri, 6 Dec 2024 12:19:29 -0300 Subject: [PATCH] PlatformGraphics: Make all Nokia's drawPixels methods more robust Nokia DirectGraphics states that width and height CAN be zero, which would cause problems with MIDP's image handling that expects them to be always bigger than zero. Fortunately, we can just return from these without doing anything, as the resulting "image" wouldn't be visible anyway. Besides that, drawPixels(byte) now properly checks for bounds on TYPE_BYTE_1_GRAY, since previously the set/getDataElements usage allowed for the data array to have its size increased specifically for this format, which is inefficient, and not possible at all now that dataBuffers are being used. So far, none of the Nokia games i've tested have been broken by these changes. In fact, Prince of Persia Harem Adventures on the Nokia 3410 was fixed by them. --- .../recompile/mobile/PlatformGraphics.java | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/org/recompile/mobile/PlatformGraphics.java b/src/org/recompile/mobile/PlatformGraphics.java index 19c1eb84..1abd5a4b 100644 --- a/src/org/recompile/mobile/PlatformGraphics.java +++ b/src/org/recompile/mobile/PlatformGraphics.java @@ -452,14 +452,19 @@ public void drawImage(javax.microedition.lcdui.Image img, int x, int y, int anch public void drawPixels(byte[] pixels, byte[] transparencyMask, int offset, int scanlength, int x, int y, int width, int height, int manipulation, int format) { + if (width == 0 || height == 0) { return; } + if (width < 0 || height < 0) { throw new IllegalArgumentException("drawPixels(byte) received negative width or height"); } + if (pixels == null) { throw new NullPointerException("drawPixels(byte) received a null pixel array"); } + if (offset < 0 || offset >= (pixels.length * 8)) { throw new ArrayIndexOutOfBoundsException("drawPixels(byte) index out of bounds:" + width + " * " + height + "| pixels len:" + (pixels.length * 8) + "| offset:" + offset); } + int[] Type1 = {0xFFFFFFFF, 0xFF000000, 0x00FFFFFF, 0x00000000}; int c = 0; - BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);// Nokia DirectGraphics states that image width and height CAN be zero. final int[] data = ((DataBufferInt) temp.getRaster().getDataBuffer()).getData(); switch (format) { - case -1: // TYPE_BYTE_1_GRAY_VERTICAL - Used by Munkiki's Castles + case DirectGraphics.TYPE_BYTE_1_GRAY_VERTICAL: // TYPE_BYTE_1_GRAY_VERTICAL - Used by Munkiki's Castles int ods = offset / scanlength; int oms = offset % scanlength; int b = ods % 8; // Bit offset in a byte @@ -472,7 +477,7 @@ public void drawPixels(byte[] pixels, byte[] transparencyMask, int offset, int s c = ((pixels[tmp + xj] >> b) & 1); if (transparencyMask != null) { - c |= (((transparencyMask[tmp + xj] >> b) & 1) ^ 1) << 1; + c |= (((transparencyMask[tmp + xj] >> b) & 1) ^ 1) << 1; // Apply transparency mask } data[ypos + xj] = Type1[c]; // Set pixel directly in the DataBuffer also removing the need for setDataElements } @@ -481,17 +486,22 @@ public void drawPixels(byte[] pixels, byte[] transparencyMask, int offset, int s } break; - case 1: // TYPE_BYTE_1_GRAY - Also used by Munkiki's Castles + case DirectGraphics.TYPE_BYTE_1_GRAY: // TYPE_BYTE_1_GRAY - Also used by Munkiki's Castles for (int i = (offset / 8); i < pixels.length; i++) { for (int j = 7; j >= 0; j--) { + int pixelIndex = (i * 8) + (7 - j); + + // Ensure we don't exceed data length based on image's width and height (as here the pixel can go out of bounds) + if (pixelIndex >= width * height) { break; } + c = ((pixels[i] >> j) & 1); if (transparencyMask != null) { - c |= (((transparencyMask[i] >> j) & 1) ^ 1) << 1; + c |= (((transparencyMask[i] >> j) & 1) ^ 1) << 1; } - data[(i * 8) + (7 - j)] = Type1[c]; // Same as above + data[pixelIndex] = Type1[c]; } } break; @@ -504,9 +514,10 @@ public void drawPixels(byte[] pixels, byte[] transparencyMask, int offset, int s public void drawPixels(int[] pixels, boolean transparency, int offset, int scanlength, int x, int y, int width, int height, int manipulation, int format) { - if (width <= 0 || height <= 0) { return; } - if (pixels == null) { throw new NullPointerException(); } - if (offset < 0 || offset >= pixels.length) { throw new ArrayIndexOutOfBoundsException(); } + if (width == 0 || height == 0) { return; } + if (width < 0 || height < 0) { throw new IllegalArgumentException("drawPixels(int) received negative width or height"); } + if (pixels == null) { throw new NullPointerException("drawPixels(int) received a null pixel array"); } + if (offset < 0 || offset >= pixels.length) { throw new ArrayIndexOutOfBoundsException("drawPixels(int) index out of bounds:" + width + " * " + height + "| len:" + pixels.length); } if (scanlength > 0) { @@ -537,6 +548,11 @@ public void drawPixels(int[] pixels, boolean transparency, int offset, int scanl public void drawPixels(short[] pixels, boolean transparency, int offset, int scanlength, int x, int y, int width, int height, int manipulation, int format) { + if (width == 0 || height == 0) { return; } + if (width < 0 || height < 0) { throw new IllegalArgumentException("drawPixels(short) received negative width or height"); } + if (pixels == null) { throw new NullPointerException("drawPixels(short) received a null pixel array"); } + if (offset < 0 || offset >= pixels.length) { throw new ArrayIndexOutOfBoundsException("drawPixels(short) index out of bounds:" + width + " * " + height + "| len:" + pixels.length); } + BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); int[] data = ((DataBufferInt) temp.getRaster().getDataBuffer()).getData();