bookmark_borderOptimal RGB16 BMP editing in Python

ImageManager class, to manage and abstract Image type.

It allows automatic management of the next pixel to be updated with a defined set of constraints :

  • Value of y : TOP/BOTTOM
  • Value of x : LEFT/RIGHT
  • Direction : HORIZONTAL/VERTICAL
from struct import pack,unpack
class ImageManager:
    # Fill x, or y first
    HORIZONTAL = 0
    VERTICAL = 1
    # Start y = 0, or y = height-1
    TOP = 0
    BOTTOM = 1
    # Start x = 0, or x = width-1
    LEFT = 0
    RIGHT = 1
    def __init__(self, _name, _width, _height, _background_color = int('0x0000', 16), _x_mode = LEFT, _y_mode = TOP, _direction_mode = HORIZONTAL):
        self.name = _name
        self.width = _width
        self.height = _height
        self.background_color = _background_color
        self.x_mode = _x_mode
        self.y_mode = _y_mode
        self.direction_mode = _direction_mode
        self.is_finished = False
        self.x = 0 if self.x_mode == ImageManager.TOP else (self.width - 1)
        self.y = 0 if self.y_mode == ImageManager.LEFT else (self.height - 1)
        
        self.reset()
    # Initialize a new Image with specified background
    def reset(self):
        self.image = Bitmap(self.width, self.height, self.background_color)
    # Update corresponding pixel in memory
    def update(self, _x, _y, _pixel):
        self.image.set_pixel(_x, _y, _pixel)
    # Update only corresponding byte in already existing Image file
    def raw_update(self, _x, _y, _pixel):
        self.image.write_pixel(_x, _y, _pixel, self.name +".bmp")
    # Manage next pixel using specified configuration
    def add(self, _pixel):
        if ( self.is_finished is True ):
            raise Exception("Image size limit exceeded.")
        else:            
            self.update(self.x, self.y, _pixel)
            if ( self.direction_mode == ImageManager.HORIZONTAL ):
                isNextRow = False
                if ( self.x_mode == ImageManager.LEFT):
                    if ( self.x == (self.width-1) ):
                        self.x = 0
                        isNextRow = True
                    else:
                        self.x = self.x + 1
                else:
                    if ( self.x == 0 ):
                        self.x = (self.width-1)
                        isNextRow = True
                    else:
                        self.x = self.x - 1
                if ( isNextRow == True ):
                    if ( self.y_mode == ImageManager.TOP):
                        self.y = self.y + 1
                    else:
                        self.y = self.y - 1
            else:
                isNextColumn = False
                if ( self.y_mode == ImageManager.TOP):
                    if ( self.y == (self.height-1) ):
                        self.y = 0
                        isNextColumn = True
                    else:
                        self.y = self.y + 1
                else:
                    if ( self.y == 0 ):
                        self.y = self.height - 1
                        isNextColumn = True
                    else:
                        self.y = self.y - 1
                if ( isNextColumn == True ):
                    if ( self.x_mode == ImageManager.LEFT):
                        self.x = self.x + 1
                    else:
                        self.x = self.x - 1
            if (
                (self.x_mode == ImageManager.TOP and self.y_mode == ImageManager.LEFT and self.x == (self.width-1) and self.y == (self.height-1)) or
                (self.x_mode == ImageManager.TOP and self.y_mode == ImageManager.RIGHT and self.x == (self.width-1) and self.y == 0) or
                (self.x_mode == ImageManager.BOTTOM and self.y_mode == ImageManager.RIGHT and self.x == 0 and self.y == 0) or
                (self.x_mode == ImageManager.BOTTOM and self.y_mode == ImageManager.LEFT and self.x == 0 and self.y == (self.height-1))      
            ):
                self.is_finished = True
            
    def render(self):
        self.image.write( self.name +".bmp" )

Bitmap class, to render a RGB16 bitmap or update an existing bitmap with a raw byte change.

class Bitmap:
    def __init__(self, width, height, background_color):
        self.TYPE = 19778 # Bitmap signature : BM
        self.RESERVED_1 = 0 
        self.RESERVED_2 = 0
        self.DATA_OFFSET = 54 # Data starting offset
        self.SIZE = self.DATA_OFFSET + width * 2 * height
        # BITMAPINFOHEADER
        self.HEADER_SIZE = 40 # BITMAPINFOHEADER size
        self.WIDTH = width
        self.HEIGHT = height
        self.PLANES = 1 # Must be 1
        self.BPP = 16 # Number of bits per pixel
        self.COMPRESSION = 0 # Compression method, 0 = BI_RGB (no compression)
        self.IMAGE_SIZE = 0 # BI_RGB, dummy zero is allowed
        self.H_RESOLUTION = 0 # Horizontal pixel per meter
        self.V_RESOLUTION = 0 # Vertical pixel per meter
        self.COLORS_IN_PALETTE = 0 # Number of colors in color palette, 0 default to 2^n
        self.IMPORTANT_COLOR = 0 # Number of important color, 0 is every color is important
        # Out of spec, utility
        self.background_color = background_color
        self.clear()
    def clear(self):
        self.data = [pack('H', self.background_color)] * self.WIDTH * self.HEIGHT
    def set_pixel(self, x, y, color):
        if ( x < 0 or y < 0 or x > (self.WIDTH-1) or y > (self.HEIGHT-1) ):
            raise ValueError("["+ x +";"+ y +"] is out of range.")
        self.data[y * self.WIDTH + x] = pack('<H', color)
    def write_pixel(self, x, y, color, file):
        if ( x < 0 or y < 0 or x > (self.WIDTH-1) or y > (self.HEIGHT-1) ):
            raise ValueError("["+ str(x) +";"+ str(y) +"] is out of range.")
        with open(file, 'rb+') as f:
            f.seek( self.DATA_OFFSET, 0)
            f.seek( (self.HEIGHT-1-y)*self.HEIGHT*(self.BPP/8) + x * (self.BPP/8), 1)
            f.write( pack('<H', color) )   
    def write(self, file):
        with open(file, 'wb') as f:
            # Writing BITMAPFILEHEADER
            f.write(pack('<HLHHL', 
                    self.TYPE, 
                    self.SIZE, 
                    self.RESERVED_1, 
                    self.RESERVED_2, 
                    self.DATA_OFFSET)) 
            # Writing BITMAPINFO
            f.write(pack('<LLLHHLLLLLL', 
                    self.HEADER_SIZE, 
                    self.WIDTH, 
                    self.HEIGHT, 
                    self.PLANES, 
                    self.BPP,
                    self.COMPRESSION, # No compression
                    self.IMAGE_SIZE, # Raw size, as it is a BI_RGB a dummy 0 can be set
                    self.H_RESOLUTION, # Horizontal pixel per meter
                    self.V_RESOLUTION, # Vertical pixel per meter
                    self.COLORS_IN_PALETTE, # Number of color, default will be 2^n
                    self.IMPORTANT_COLOR  # Important color
                    ))
            # Writing data
            for pixel in self.data:
                tarte = unpack('<H', pixel)
                f.write(pixel)
            # Padding data
            for i in range( (4 - ( (self.WIDTH*3) % 4) ) % 4):
                f.write( pack('B', 0) )

Small example :

# R G B
red = int('0x7C00', 16)
white = int('0x7FFF', 16)
# In BMP, TOP and BOTTOM are reverse !
myImage = ImageManager("test", 200,200, int('0x03E0', 16), ImageManager.LEFT, ImageManager.TOP, ImageManager.HORIZONTAL)
i = 0
while ( i < 500 ):
    myImage.add(red)
    i = i + 1
# Save image
myImage.render()
myImage.raw_update(0, 0, white)
myImage.raw_update(0, 19, white)
myImage.raw_update(19, 0, white)
myImage.raw_update(19, 19, white)
x = 10
while ( x < 20 ):
    y = 10
    while ( y < 20 ):
        myImage.raw_update(x, y, white)
        y = y + 1 
    x = x + 1

bookmark_borderXSLT 2.0 – Register result of a nested for-each to an iterable variable

Example of data structure :

<root>
    <object name="test">
        <package name="a">
            <loot>
                <item value="1" />
                <item value="2" />
            </loot>
        </package>
        <package name="b">
            <loot>
                <item value="5" />
                <item value="6" />
            </loot>
        </package>
        <session name="amazon">
            <packages>
                <package name="a" />
                <package name="b" />
            </packages>
        </session>
        <session name="wish">
            <packages>
                <package name="a" />
                <package name="b" />
            </packages>
        </session>
    </object>
</root>

To extract an iterable list of all items related to all packages specified in a session :

<xsl:variable name="items">
    <xsl:for-each select="//object/session[@name='amazon']/packages/package">
        <xsl:for-each select="//object/package[@name=current()/@name]/loot/item">
            <xsl:variable name="index">
                <xsl:number level="any" count="item"/>
            </xsl:variable>
            <xsl:if test="number($index)!=1">
                <xsl:text>,</xsl:text>
            </xsl:if>
            <xsl:value-of select="current()/@value"/>
        </xsl:for-each>
    </xsl:for-each>
</xsl:variable>
<xsl:for-each select="tokenize($items, ',')">
    <p>
        <xsl:value-of select="." />
    </p>
</xsl:for-each>

Result :

<p>1</p>
<p>2</p>
<p>5</p>
<p>6</p>

bookmark_borderQuickly configure your sysprep with unattend.xml

Details:

  • SkipRearm permits unlimited sysprepping;
  • CopyProfile make your audit profile the default profile;
  • TimeZone sets … the Timezone;
  • HideEULAPage skips the acceptance of the EULA;
  • The Display part automatically sets the maximum resolution with standard icons;
  • Last four parameters set all my locale to French.

How to use:

  • Create an unattend.xml file with the following file content;
  • With administrator rights, execute :

“C:\Windows\system32\sysprep\sysprep.exe” /oobe /generalize /unattend:[PATH_TO_YOUR_XML]”

"C:\Windows\system32\sysprep\sysprep.exe" /oobe /generalize /unattend:[PATH_TO_YOUR_XML]"

File content:

<?xml version="1.0" encoding="utf-8"?>
<unattend
  xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="generalize">
    <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <SkipRearm>1</SkipRearm>
    </component>
  </settings>
  <component name="Microsoft-Windows-PnpSysprep" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
    xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
  </component>
  <settings pass="specialize">
    <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <SkipAutoActivation>true</SkipAutoActivation>
    </component>
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <CopyProfile>true</CopyProfile>
      <TimeZone>Romance Standard Time</TimeZone>
    </component>
  </settings>
  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <OOBE>
        <HideEULAPage>true</HideEULAPage>
      </OOBE>
      <Display>
        <ColorDepth>32</ColorDepth>
        <DPI>96</DPI>
        <HorizontalResolution>1920</HorizontalResolution>
        <RefreshRate>60</RefreshRate>
        <VerticalResolution>1080</VerticalResolution>
      </Display>
    </component>
    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <InputLocale>fr-fr</InputLocale>
      <SystemLocale>fr-fr</SystemLocale>
      <UILanguage>fr-fr</UILanguage>
      <UserLocale>fr-fr</UserLocale>
    </component>
  </settings>
</unattend>

bookmark_borderInstall steamcmd on 64-bits server

# AS ROOT

dpkg --add-architecture i386 apt-get update apt-get install ia32-libs

# AS USER

wget http://blog.counter-strike.net/wp-content/uploads/2012/04/steamcmd.tar.gz
tar xvfz steamcmd.tar.gz
mkdir steamcmd
mv linux32 steamcmd
mv steam.sh steamcmd
cd steamcmd
LD_LIBRARY_PATH=/home/steam/steamcmd
STEAMEXE=steamcmd
./steam.sh