All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.pawelkrol.Afterimage.Format.Format.scala Maven / Gradle / Ivy

The newest version!
package com.github.pawelkrol.Afterimage
package Format

import java.io.{File,PrintStream}
import java.nio.file.FileAlreadyExistsException

import Config.Offset
import Memory.Address
import Mode.{CBM,HiRes,MultiColour}
import Mode.Data.{Bitmap,Screen}
import Util.ArrayHelper.deep

/** CBM graphic format abstraction functioning as a base class for miscellaneous image formats providing data validation options as well as convenient access to the file loading address and data bytes. */
trait Format {

  /** CBM file loading address that needs to be provided by constructor of a derived class. */
  val addr: Address

  /** Array of file data bytes that needs to be provided by constructor of a derived class. */
  val data: Array[Byte]

  /** Default file loading address that needs to be defined as a property of a derived class is used to validate constructor parameters. */
  val load: Address

  /** Default length of file data bytes that needs to be defined as a property of a derived class is used to validate constructor parameters. */
  val size: Int

  /** Memory configuration of the file format data that needs to be defined as a property of a derived class is used to extract image data from file data bytes. */
  val config: Offset

  /** Validates consistency of an object instance data. */
  def validate(): Unit = {
    require(
      load == addr && data.length == size,
      "Invalid image data: Not a %s format".format(this.getClass.getName.split("\\.").last)
    )
  }

  private def serialize() = addr.toWrite ++ data

  /** Save any supported CBM image format into a file.
    *
    * @param name target file name
    * @param overwriteIfExists boolean flag indicating whether overwriting of an existing file should trigger no error
    */
  def save(name: String, overwriteIfExists: Boolean = false): Unit = {

    val file = new File(name)

    if (!overwriteIfExists)
      if (file.exists())
        throw new FileAlreadyExistsException(name)

    val writer = new PrintStream(file)
    writer.write(serialize())
    writer.close()
  }

  /** Extract image data from file data bytes.
    *
    * @return a new instance of any `Mode`-inherited class with the image mode determined by the file data
    */
  def extractData(): CBM = {

    config match {
      case hiRes: Config.HiRes => {
        val bitmapData = data.slice(hiRes.bitmap, hiRes.bitmap + HiRes.size("bitmap"))
        val bitmap = Bitmap(bitmapData, Bitmap.maxCols, Bitmap.maxRows)

        val screen: Option[Screen] = hiRes match {
          case artStudio: Config.ArtStudio => {
            val screenData = data.slice(artStudio.screen, artStudio.screen + HiRes.size("screen"))
            Some(Screen(screenData, Screen.maxCols, Screen.maxRows))
          }
          case hiResBitmap: Config.HiResBitmap => None
          case _ => throw new RuntimeException("Something went wrong...")
        }

        val border = hiRes match {
          case artStudio: Config.ArtStudio => None
          case hiResBitmap: Config.HiResBitmap => None
          case _ => throw new RuntimeException("Something went wrong...")
        }

        new HiRes(bitmap, screen, border)
      }

      case multiColour: Config.MultiColour => {
        val bitmapData = data.slice(multiColour.bitmap, multiColour.bitmap + MultiColour.size("bitmap"))
        val bitmap = Bitmap(bitmapData, Bitmap.maxCols, Bitmap.maxRows)

        val screenData = data.slice(multiColour.screen, multiColour.screen + MultiColour.size("screen"))
        val screen = Screen(screenData, Screen.maxCols, Screen.maxRows)

        val colorsData = data.slice(multiColour.colors, multiColour.colors + MultiColour.size("colors"))
        val colors = Screen(colorsData, Screen.maxCols, Screen.maxRows)

        // Make sure that background colour is not initialized with value >$0f:
        val bckgrd = (data(multiColour.bckgrd).toInt & 0x0f).toByte

        val border = multiColour match {
          case advancedArtStudio: Config.AdvancedArtStudio => None
          case amicaPaint: Config.AmicaPaint => None
          case facePainter: Config.FacePainter => Some(data(facePainter.border))
          case koalaPainter: Config.KoalaPainter => None
          case _ => throw new RuntimeException("Something went wrong...")
        }

        new MultiColour(bitmap, screen, colors, border, bckgrd)
      }

      case _ => throw new RuntimeException("Something went wrong...")
    }
  }

  /** Compares the receiver object (`this`) with the argument object (`other`) for equivalence.
    *
    * @return `true` if the receiver object is equivalent to the argument, `false` otherwise
    */
  override def equals(other: Any) = other match {
    case that: Format =>
      (that canEqual this) && (this.addr == that.addr) && (deep(this.data) == deep(that.data))
    case _ =>
      false
  }

  /** States that objects of this class are never equal to objects of its superclass.
    *
    * @param that the value being probed for possible equality
    * @return `true` if `this` instance can possibly equal `that`, otherwise `false`
    */
  def canEqual(that: Any) = that.isInstanceOf[Format]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy