private void StoreButton_Click(object sender, EventArgs e) { StoreButton.Enabled = false; string extension = Path.GetExtension(FileNameTextBox.Text).ToLower(); bool binFile = !extension.Equals("bmp"); if (!binFile) { // TODO: determine what to do with the control register controlByte = (byte)((LUTCombo.SelectedIndex << 1) + 1); Memory.WriteByte(controlRegisterAddress, controlByte); // enable } // Store the address in the pointer address - little endian - 24 bits string strAddress = LoadAddressTextBox.Text.Replace(":", ""); int videoAddress = 0; if (strAddress != String.Empty) { int.TryParse(strAddress, System.Globalization.NumberStyles.HexNumber, System.Globalization.NumberFormatInfo.CurrentInfo, out videoAddress); } int writeVideoAddress = videoAddress; ResourceChecker.Resource res = new ResourceChecker.Resource { StartAddress = videoAddress, SourceFile = FileNameTextBox.Text, Name = Path.GetFileNameWithoutExtension(FileNameTextBox.Text) }; if (!binFile) { // Store the bitmap at the user's determined address // The method below simply takes the file and writes it in memory. // What we want is the actual pixels. ImageConverter converter = new ImageConverter(); byte[] data = (byte[])converter.ConvertTo(bitmap, typeof(byte[])); int startOffset = BitConverter.ToInt32(data, 10); int fileLength = BitConverter.ToInt32(data, 2); res.Length = bitmap.Height * bitmap.Width; if (ResChecker.Add(res)) { // The addresses in Vicky a offset by $B0:0000 videoAddress = videoAddress - 0xB0_0000; Memory.WriteByte(pointerAddress, LowByte(videoAddress)); Memory.WriteByte(pointerAddress + 1, MidByte(videoAddress)); Memory.WriteByte(pointerAddress + 2, HighByte(videoAddress)); // Store the strides in the strideX and strideY Memory.WriteByte(strideXAddress, LowByte(strideX)); Memory.WriteByte(strideXAddress + 1, MidByte(strideX)); Memory.WriteByte(strideYAddress, LowByte(strideY)); Memory.WriteByte(strideYAddress + 1, MidByte(strideY)); int numberOfColors = BitConverter.ToInt32(data, 46); int lutOffset = 0xAF_2000 + LUTCombo.SelectedIndex * 1024; if (numberOfColors == 0) { // we need to create a LUT - each LUT only accepts 256 entries - 0 is black TransformBitmap(data, startOffset, Int32.Parse(PixelDepthValueLabel.Text), lutOffset, writeVideoAddress, bitmap.Width, bitmap.Height); } else { for (int offset = 54; offset < 1024 + 54; offset = offset + 4) { int color = BitConverter.ToInt32(data, offset); Memory.WriteByte(lutOffset, LowByte(color)); Memory.WriteByte(lutOffset + 1, MidByte(color)); Memory.WriteByte(lutOffset + 2, HighByte(color)); Memory.WriteByte(lutOffset + 3, 0xFF); // Alpha lutOffset = lutOffset + 4; } for (int line = 0; line < bitmap.Height; line++) { for (int i = 0; i < bitmap.Width; i++) { Memory.WriteByte(writeVideoAddress + (bitmap.Height - line + 1) * bitmap.Width + i, data[startOffset + line * bitmap.Width + i]); } } } if (BitmapTypesCombo.SelectedIndex > 0 && BitmapTypesCombo.SelectedIndex < 5) { int layer = BitmapTypesCombo.SelectedIndex - 1; OnTileLoaded?.Invoke(layer); } MessageBox.Show("Transfer successful!", "Bitmap Storage", MessageBoxButtons.OK, MessageBoxIcon.Information); this.Close(); } else { StoreButton.Enabled = true; } } else { byte[] data = File.ReadAllBytes(FileNameTextBox.Text); for (int i = 0; i < data.Length; i++) { Memory.WriteByte(videoAddress + i, data[i]); } StoreButton.Enabled = true; } }
private unsafe void ConvertBitmapToRaw(Bitmap bitmap, ResourceChecker.Resource resource, byte lutIndex, int stride, int maxHeight) { if (ResChecker.Add(resource)) { // Load LUT from memory - ignore indexes 0 and 1 int lutBaseAddress = MemoryLocations.MemoryMap.GRP_LUT_BASE_ADDR + lutIndex * 0x400 - MemoryLocations.MemoryMap.VICKY_BASE_ADDR; // Limit how much data is imported based on the type of image int importedLines = maxHeight < bitmap.Height ? maxHeight : bitmap.Height; int importedCols = stride < bitmap.Width ? stride : bitmap.Width; byte[] data = new byte[stride * importedLines]; // the bitmap is based on resolution of the machine resource.Length = stride * bitmap.Height; // one byte per pixel - palette is separate Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); int bytesPerPixel = bitmapData.Stride / bitmap.Width; byte * bitmapPointer = (byte *)bitmapData.Scan0.ToPointer(); bool tooManyColours = false; bool done = false; byte mask = 0xFF; List <int> lut = null; while (!done && mask != 0xc0) { int transparentColor = 0; try { transparentColor = Convert.ToInt32(textTransparentColor.Text, 16) & ((mask << 16) + (mask << 8) + mask); } finally { } done = true; // Reset the Lookup Table lut = new List <int>(256) { // Always add black (transparent) and white 0, 0xFFFFFF }; // The user may decide to overwrite the palette from this bitmap if (!checkOverwriteLUT.Checked) { for (int i = 2; i < 256; i++) { int value = MemMgrRef.VICKY.ReadLong(lutBaseAddress + 4 * i); if (value != 0) { lut.Add(value); } else { break; } } } for (int line = 0; line < importedLines; line++) { for (int col = 0; col < importedCols; col++) { byte b = 0; byte r = 0; byte g = 0; switch (bytesPerPixel) { case 1: byte palIndex = bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel]; System.Drawing.Color palValue = bitmap.Palette.Entries[palIndex]; b = (byte)(palValue.B & mask); g = (byte)(palValue.G & mask); r = (byte)(palValue.R & mask); break; case 2: ushort wordValue = (ushort)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel] + bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel + 1] * 256); b = (byte)(wordValue & 0x1F); // 5bits g = (byte)((wordValue >> 5) & 0x3F); // 6 bits r = (byte)(wordValue >> 11); // 5 bits break; case 3: b = (byte)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel] & mask); g = (byte)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel + 1] & mask); r = (byte)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel + 2] & mask); break; case 4: b = (byte)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel] & mask); g = (byte)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel + 1] & mask); r = (byte)(bitmapPointer[line * bitmapData.Stride + col * bytesPerPixel + 2] & mask); //alpha is ignored break; } int rgb = b + g * 256 + r * 256 * 256; // Check if the RBG matches the transparent color int index = 0; if (rgb != transparentColor) { // if not, look in the LUT index = lut.IndexOf(rgb); } // If the index is undefined, add a new entry if (index == -1) { if (lut.Count < 256) { lut.Add(rgb); index = (byte)lut.IndexOf(rgb); } else { tooManyColours = true; break; } } if (index != -1) { data[line * stride + col] = (byte)index; } } if (tooManyColours) { // TODO should use a colour histogram to count how many times a colour is used and then decimate here, based on low usage. done = false; tooManyColours = false; mask <<= 1; break; } } } if (mask != 0xc0) { int videoAddress = resource.StartAddress - 0xB0_0000; MemMgrRef.VIDEO.CopyBuffer(data, 0, videoAddress, data.Length); if (lut != null) { for (int i = 0; i < lut.Count; i++) { int rbg = lut[i]; MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i, LowByte(rbg)); MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 1, MidByte(rbg)); MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 2, HighByte(rbg)); } } // Check if a LUT matching our index is present in the Resources, if so don't do anything. Resource resLut = ResChecker.Find(ResourceType.lut, lutBaseAddress + MemoryLocations.MemoryMap.VICKY_BASE_ADDR); if (resLut == null) { Resource lutPlaceholder = new Resource { Length = 0x400, FileType = ResourceType.lut, Name = "Generated LUT", StartAddress = lutBaseAddress + MemoryLocations.MemoryMap.VICKY_BASE_ADDR }; ResChecker.Add(lutPlaceholder); } } else { MessageBox.Show("An error occured converting the image colors to LUT.\n" + "You can try loading the image with a different LUT or\n" + "Zero one of the LUTs or\n" + "Check the Overwrite Existing LUT checkbox"); ResChecker.Items.Remove(resource); resource.Length = -1; } } else { resource.Length = -1; } }
private void StoreButton_Click(object sender, EventArgs e) { StoreButton.Enabled = false; // Store the address in the pointer address - little endian - 24 bits int destAddress = Convert.ToInt32(LoadAddressTextBox.Text.Replace(":", ""), 16); byte[] data = File.ReadAllBytes(FileNameTextBox.Text); for (int i = 0; i < data.Length; i++) { Memory.WriteByte(destAddress + i, data[i]); } // Determine which addresses to store the bitmap into. if (FileTypesCombo.SelectedIndex == 0) { // Raw } else if (FileTypesCombo.SelectedIndex < 5) { // Tilemaps 4 int tilemapIndex = FileTypesCombo.SelectedIndex - 1; int baseAddress = MemoryLocations.MemoryMap.TILE_CONTROL_REGISTER_ADDR + tilemapIndex * 12; byte lutValue = (byte)LUTCombo.SelectedIndex; // enable the tilemap Memory.WriteByte(baseAddress, (byte)(1 + (lutValue << 1))); // write address offset by bank $b0 int offsetAddress = destAddress - 0xB0_0000; Memory.WriteByte(baseAddress + 1, (byte)(offsetAddress & 0xFF)); Memory.WriteByte(baseAddress + 2, (byte)((offsetAddress & 0xFF00) >> 8)); Memory.WriteByte(baseAddress + 3, (byte)((offsetAddress & 0xFF_0000) >> 16)); // TODO: Need to write the size of the tilemap } else if (FileTypesCombo.SelectedIndex < 13) { // Tilesets 8 int tilesetIndex = FileTypesCombo.SelectedIndex - 5; int baseAddress = MemoryLocations.MemoryMap.TILESET_BASE_ADDR + tilesetIndex * 4; byte lutValue = (byte)LUTCombo.SelectedIndex; // write address offset by bank $b0 int offsetAddress = destAddress - 0xB0_0000; Memory.WriteByte(baseAddress, (byte)(offsetAddress & 0xFF)); Memory.WriteByte(baseAddress + 1, (byte)((offsetAddress & 0xFF00) >> 8)); Memory.WriteByte(baseAddress + 2, (byte)((offsetAddress & 0xFF_0000) >> 16)); Memory.WriteByte(baseAddress + 3, lutValue); // TODO: Add the stride 256 bit 3. } else { // Sprites 64 int spriteIndex = FileTypesCombo.SelectedIndex - 13; int baseAddress = MemoryLocations.MemoryMap.SPRITE_CONTROL_REGISTER_ADDR + spriteIndex * 8; byte lutValue = (byte)LUTCombo.SelectedIndex; // enable the tilemap Memory.WriteByte(baseAddress, (byte)(1 + (lutValue << 1))); // TODO: Add sprite depth // write address offset by bank $b0 int offsetAddress = destAddress - 0xB0_0000; Memory.WriteByte(baseAddress + 1, (byte)(offsetAddress & 0xFF)); Memory.WriteByte(baseAddress + 2, (byte)((offsetAddress & 0xFF00) >> 8)); Memory.WriteByte(baseAddress + 3, (byte)((offsetAddress & 0xFF_0000) >> 16)); // TODO: set the position of the sprite } ResourceChecker.Resource res = new ResourceChecker.Resource { StartAddress = destAddress, SourceFile = FileNameTextBox.Text, Name = Path.GetFileNameWithoutExtension(FileNameTextBox.Text), FileType = FileTypesCombo.SelectedIndex }; StoreButton.Enabled = true; //// Store the bitmap at the user's determined address //// The method below simply takes the file and writes it in memory. //// What we want is the actual pixels. //ImageConverter converter = new ImageConverter(); //byte[] data = (byte[])converter.ConvertTo(bitmap, typeof(byte[])); //int startOffset = BitConverter.ToInt32(data, 10); //int fileLength = BitConverter.ToInt32(data, 2); //res.Length = bitmap.Height * bitmap.Width; //if (ResChecker.Add(res)) //{ // // The addresses in Vicky a offset by $B0:0000 // videoAddress = videoAddress - 0xB0_0000; // Memory.WriteByte(pointerAddress, LowByte(videoAddress)); // Memory.WriteByte(pointerAddress + 1, MidByte(videoAddress)); // Memory.WriteByte(pointerAddress + 2, HighByte(videoAddress)); // // Store the strides in the strideX and strideY // Memory.WriteByte(strideXAddress, LowByte(strideX)); // Memory.WriteByte(strideXAddress + 1, MidByte(strideX)); // Memory.WriteByte(strideYAddress, LowByte(strideY)); // Memory.WriteByte(strideYAddress + 1, MidByte(strideY)); // int numberOfColors = BitConverter.ToInt32(data, 46); // int lutOffset = 0xAF_2000 + LUTCombo.SelectedIndex * 1024; // if (numberOfColors == 0) // { // // we need to create a LUT - each LUT only accepts 256 entries - 0 is black // TransformBitmap(data, startOffset, Int32.Parse(PixelDepthValueLabel.Text), lutOffset, writeVideoAddress, bitmap.Width, bitmap.Height); // } // else // { // for (int offset = 54; offset < 1024 + 54; offset = offset + 4) // { // int color = BitConverter.ToInt32(data, offset); // Memory.WriteByte(lutOffset, LowByte(color)); // Memory.WriteByte(lutOffset + 1, MidByte(color)); // Memory.WriteByte(lutOffset + 2, HighByte(color)); // Memory.WriteByte(lutOffset + 3, 0xFF); // Alpha // lutOffset = lutOffset + 4; // } // for (int line = 0; line < bitmap.Height; line++) // { // for (int i = 0; i < bitmap.Width; i++) // { // Memory.WriteByte(writeVideoAddress + (bitmap.Height - line + 1) * bitmap.Width + i, data[startOffset + line * bitmap.Width + i]); // } // } // } // if (FileTypesCombo.SelectedIndex > 0 && FileTypesCombo.SelectedIndex < 5) // { // int layer = FileTypesCombo.SelectedIndex - 1; // OnTileLoaded?.Invoke(layer); // } // MessageBox.Show("Transfer successful!", "Bitmap Storage", MessageBoxButtons.OK, MessageBoxIcon.Information); // this.Close(); //} //else //{ // StoreButton.Enabled = true; //} }
private void StoreButton_Click(object sender, EventArgs e) { StoreButton.Enabled = false; // Store the address in the pointer address - little endian - 24 bits int destAddress = Convert.ToInt32(LoadAddressTextBox.Text.Replace(":", ""), 16); FileInfo info = new FileInfo(FileNameTextBox.Text); byte MCRHigh = (byte)(MemMgrRef.VICKY.ReadByte(1) & 3); int screenResX = 640; int screenResY = 480; switch (MCRHigh) { case 1: screenResX = 800; screenResY = 600; break; case 2: screenResX = 320; screenResY = 240; break; case 3: screenResX = 400; screenResY = 300; break; } ResourceType operationType = ResourceType.raw; int conversionStride = 0; int maxHeight = screenResY; if (FileTypesCombo.SelectedIndex < 2) { operationType = ResourceType.bitmap; conversionStride = screenResX; } else if (FileTypesCombo.SelectedIndex < 6) { operationType = ResourceType.tilemap; ExtLabel.Text = ".data"; } else if (FileTypesCombo.SelectedIndex < 14) { operationType = ResourceType.tileset; conversionStride = 256; maxHeight = 256; } else if (FileTypesCombo.SelectedIndex < 78) { operationType = ResourceType.sprite; conversionStride = 32; maxHeight = 32; } else { operationType = ResourceType.lut; ExtLabel.Text = ".data"; } ResourceChecker.Resource res = new ResourceChecker.Resource { StartAddress = destAddress, SourceFile = FileNameTextBox.Text, Name = Path.GetFileNameWithoutExtension(FileNameTextBox.Text), FileType = operationType, }; switch (ExtLabel.Text.ToLower()) { case ".png": Bitmap png = new Bitmap(FileNameTextBox.Text, false); ConvertBitmapToRaw(png, res, (byte)LUTCombo.SelectedIndex, conversionStride, maxHeight); break; case ".bmp": Bitmap bmp = new Bitmap(FileNameTextBox.Text, false); ConvertBitmapToRaw(bmp, res, (byte)LUTCombo.SelectedIndex, conversionStride, maxHeight); break; default: // Read the file as raw byte[] data = File.ReadAllBytes(FileNameTextBox.Text); // Check if there's a resource conflict res.Length = data.Length; if (ResChecker.Add(res)) { MemMgrRef.CopyBuffer(data, 0, destAddress, data.Length); } else { res.Length = -1; } break; } if (res.Length > 0) { // write address offset by bank $b0 int imageAddress = destAddress - 0xB0_0000; int regAddress = -1; byte lutValue = (byte)LUTCombo.SelectedIndex; // Determine which addresses to store the bitmap into. if (FileTypesCombo.SelectedIndex < 2) { // Bitmaps regAddress = MemoryLocations.MemoryMap.BITMAP_CONTROL_REGISTER_ADDR + FileTypesCombo.SelectedIndex * 8; // enable the bitmap - TODO add the LUT MemMgrRef.WriteByte(regAddress, (byte)(1 + lutValue * 2)); } else if (FileTypesCombo.SelectedIndex < 6) { // Tilemaps 4 int tilemapIndex = FileTypesCombo.SelectedIndex - 1; regAddress = MemoryLocations.MemoryMap.TILE_CONTROL_REGISTER_ADDR + tilemapIndex * 12; // enable the tilemap MemMgrRef.WriteByte(regAddress, (byte)(1 + (lutValue << 1))); // TODO: Need to write the size of the tilemap } else if (FileTypesCombo.SelectedIndex < 14) { // Tilesets 8 int tilesetIndex = FileTypesCombo.SelectedIndex - 5; regAddress = MemoryLocations.MemoryMap.TILESET_BASE_ADDR + tilesetIndex * 4; MemMgrRef.WriteByte(regAddress + 3, lutValue); // TODO: Add the stride 256 bit 3. } else { // Sprites 64 int spriteIndex = FileTypesCombo.SelectedIndex - 14; regAddress = MemoryLocations.MemoryMap.SPRITE_CONTROL_REGISTER_ADDR + spriteIndex * 8; // enable the tilemap MemMgrRef.WriteByte(regAddress, (byte)(1 + (lutValue << 1))); // TODO: Add sprite depth // write address offset by bank $b0 // Set the sprite at (32,32) MemMgrRef.WriteWord(regAddress + 4, 32); MemMgrRef.WriteWord(regAddress + 6, 32); } // write address offset by bank $b0 MemMgrRef.WriteByte(regAddress + 1, LowByte(imageAddress)); MemMgrRef.WriteByte(regAddress + 2, MidByte(imageAddress)); MemMgrRef.WriteByte(regAddress + 3, HighByte(imageAddress)); StoreButton.Enabled = true; } if (res.Length != -1) { this.DialogResult = DialogResult.OK; if (FileTypesCombo.SelectedIndex > 1 && FileTypesCombo.SelectedIndex < 6) { int layer = FileTypesCombo.SelectedIndex - 2; //OnTileLoaded?.Invoke(layer); } Close(); } else { // Keep the Asset Loader open StoreButton.Enabled = true; } }