[
  {
    "path": "LICENCE.txt",
    "content": "\n  CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL\n\nVersion 2.1 du 2013-06-21\n\n\n    Avertissement\n\nCe contrat est une licence de logiciel libre issue d'une concertation\nentre ses auteurs afin que le respect de deux grands principes préside à\nsa rédaction:\n\n  * d'une part, le respect des principes de diffusion des logiciels\n    libres: accès au code source, droits étendus conférés aux utilisateurs,\n  * d'autre part, la désignation d'un droit applicable, le droit\n    français, auquel elle est conforme, tant au regard du droit de la\n    responsabilité civile que du droit de la propriété intellectuelle et\n    de la protection qu'il offre aux auteurs et titulaires des droits\n    patrimoniaux sur un logiciel.\n\nLes auteurs de la licence CeCILL (Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])\nsont:\n\nCommissariat à l'énergie atomique et aux énergies alternatives - CEA,\nétablissement public de recherche à caractère scientifique, technique et\nindustriel, dont le siège est situé 25 rue Leblanc, immeuble Le Ponant\nD, 75015 Paris.\n\nCentre National de la Recherche Scientifique - CNRS, établissement\npublic à caractère scientifique et technologique, dont le siège est\nsitué 3 rue Michel-Ange, 75794 Paris cedex 16.\n\nInstitut National de Recherche en Informatique et en Automatique -\nInria, établissement public à caractère scientifique et technologique,\ndont le siège est situé Domaine de Voluceau, Rocquencourt, BP 105, 78153\nLe Chesnay cedex.\n\n\n    Préambule\n\nCe contrat est une licence de logiciel libre dont l'objectif est de\nconférer aux utilisateurs la liberté de modification et de\nredistribution du logiciel régi par cette licence dans le cadre d'un\nmodèle de diffusion en logiciel libre.\n\nL'exercice de ces libertés est assorti de certains devoirs à la charge\ndes utilisateurs afin de préserver ce statut au cours des\nredistributions ultérieures.\n\nL'accessibilité au code source et les droits de copie, de modification\net de redistribution qui en découlent ont pour contrepartie de n'offrir\naux utilisateurs qu'une garantie limitée et de ne faire peser sur\nl'auteur du logiciel, le titulaire des droits patrimoniaux et les\nconcédants successifs qu'une responsabilité restreinte.\n\nA cet égard l'attention de l'utilisateur est attirée sur les risques\nassociés au chargement, à l'utilisation, à la modification et/ou au\ndéveloppement et à la reproduction du logiciel par l'utilisateur étant\ndonné sa spécificité de logiciel libre, qui peut le rendre complexe à\nmanipuler et qui le réserve donc à des développeurs ou des\nprofessionnels avertis possédant des connaissances informatiques\napprofondies. Les utilisateurs sont donc invités à charger et tester\nl'adéquation du logiciel à leurs besoins dans des conditions permettant\nd'assurer la sécurité de leurs systèmes et/ou de leurs données et, plus\ngénéralement, à l'utiliser et l'exploiter dans les mêmes conditions de\nsécurité. Ce contrat peut être reproduit et diffusé librement, sous\nréserve de le conserver en l'état, sans ajout ni suppression de clauses.\n\nCe contrat est susceptible de s'appliquer à tout logiciel dont le\ntitulaire des droits patrimoniaux décide de soumettre l'exploitation aux\ndispositions qu'il contient.\n\nUne liste de questions fréquemment posées se trouve sur le site web\nofficiel de la famille des licences CeCILL \n(http://www.cecill.info/index.fr.html) pour toute clarification qui\nserait nécessaire.\n\n\n    Article 1 - DEFINITIONS\n\nDans ce contrat, les termes suivants, lorsqu'ils seront écrits avec une\nlettre capitale, auront la signification suivante:\n\nContrat: désigne le présent contrat de licence, ses éventuelles versions\npostérieures et annexes.\n\nLogiciel: désigne le logiciel sous sa forme de Code Objet et/ou de Code\nSource et le cas échéant sa documentation, dans leur état au moment de\nl'acceptation du Contrat par le Licencié.\n\nLogiciel Initial: désigne le Logiciel sous sa forme de Code Source et\néventuellement de Code Objet et le cas échéant sa documentation, dans\nleur état au moment de leur première diffusion sous les termes du Contrat.\n\nLogiciel Modifié: désigne le Logiciel modifié par au moins une\nContribution.\n\nCode Source: désigne l'ensemble des instructions et des lignes de\nprogramme du Logiciel et auquel l'accès est nécessaire en vue de\nmodifier le Logiciel.\n\nCode Objet: désigne les fichiers binaires issus de la compilation du\nCode Source.\n\nTitulaire: désigne le ou les détenteurs des droits patrimoniaux d'auteur\nsur le Logiciel Initial.\n\nLicencié: désigne le ou les utilisateurs du Logiciel ayant accepté le\nContrat.\n\nContributeur: désigne le Licencié auteur d'au moins une Contribution.\n\nConcédant: désigne le Titulaire ou toute personne physique ou morale\ndistribuant le Logiciel sous le Contrat.\n\nContribution: désigne l'ensemble des modifications, corrections,\ntraductions, adaptations et/ou nouvelles fonctionnalités intégrées dans\nle Logiciel par tout Contributeur, ainsi que tout Module Interne.\n\nModule: désigne un ensemble de fichiers sources y compris leur\ndocumentation qui permet de réaliser des fonctionnalités ou services\nsupplémentaires à ceux fournis par le Logiciel.\n\nModule Externe: désigne tout Module, non dérivé du Logiciel, tel que ce\nModule et le Logiciel s'exécutent dans des espaces d'adressage\ndifférents, l'un appelant l'autre au moment de leur exécution.\n\nModule Interne: désigne tout Module lié au Logiciel de telle sorte\nqu'ils s'exécutent dans le même espace d'adressage.\n\nGNU GPL: désigne la GNU General Public License dans sa version 2 ou\ntoute version ultérieure, telle que publiée par Free Software Foundation\nInc.\n\nGNU Affero GPL: désigne la GNU Affero General Public License dans sa\nversion 3 ou toute version ultérieure, telle que publiée par Free\nSoftware Foundation Inc.\n\nEUPL: désigne la Licence Publique de l'Union européenne dans sa version\n1.1 ou toute version ultérieure, telle que publiée par la Commission\nEuropéenne.\n\nParties: désigne collectivement le Licencié et le Concédant.\n\nCes termes s'entendent au singulier comme au pluriel.\n\n\n    Article 2 - OBJET\n\nLe Contrat a pour objet la concession par le Concédant au Licencié d'une\nlicence non exclusive, cessible et mondiale du Logiciel telle que\ndéfinie ci-après à l'article 5 <#etendue> pour toute la durée de\nprotection des droits portant sur ce Logiciel.\n\n\n    Article 3 - ACCEPTATION\n\n3.1 L'acceptation par le Licencié des termes du Contrat est réputée\nacquise du fait du premier des faits suivants:\n\n  * (i) le chargement du Logiciel par tout moyen notamment par\n    téléchargement à partir d'un serveur distant ou par chargement à\n    partir d'un support physique;\n  * (ii) le premier exercice par le Licencié de l'un quelconque des\n    droits concédés par le Contrat.\n\n3.2 Un exemplaire du Contrat, contenant notamment un avertissement\nrelatif aux spécificités du Logiciel, à la restriction de garantie et à\nla limitation à un usage par des utilisateurs expérimentés a été mis à\ndisposition du Licencié préalablement à son acceptation telle que\ndéfinie à l'article 3.1 <#acceptation-acquise> ci dessus et le Licencié\nreconnaît en avoir pris connaissance.\n\n\n    Article 4 - ENTREE EN VIGUEUR ET DUREE\n\n\n      4.1 ENTREE EN VIGUEUR\n\nLe Contrat entre en vigueur à la date de son acceptation par le Licencié\ntelle que définie en 3.1 <#acceptation-acquise>.\n\n\n      4.2 DUREE\n\nLe Contrat produira ses effets pendant toute la durée légale de\nprotection des droits patrimoniaux portant sur le Logiciel.\n\n\n    Article 5 - ETENDUE DES DROITS CONCEDES\n\nLe Concédant concède au Licencié, qui accepte, les droits suivants sur\nle Logiciel pour toutes destinations et pour la durée du Contrat dans\nles conditions ci-après détaillées.\n\nPar ailleurs, si le Concédant détient ou venait à détenir un ou\nplusieurs brevets d'invention protégeant tout ou partie des\nfonctionnalités du Logiciel ou de ses composants, il s'engage à ne pas\nopposer les éventuels droits conférés par ces brevets aux Licenciés\nsuccessifs qui utiliseraient, exploiteraient ou modifieraient le\nLogiciel. En cas de cession de ces brevets, le Concédant s'engage à\nfaire reprendre les obligations du présent alinéa aux cessionnaires.\n\n\n      5.1 DROIT D'UTILISATION\n\nLe Licencié est autorisé à utiliser le Logiciel, sans restriction quant\naux domaines d'application, étant ci-après précisé que cela comporte:\n\n 1.\n\n    la reproduction permanente ou provisoire du Logiciel en tout ou\n    partie par tout moyen et sous toute forme.\n\n 2.\n\n    le chargement, l'affichage, l'exécution, ou le stockage du Logiciel\n    sur tout support.\n\n 3.\n\n    la possibilité d'en observer, d'en étudier, ou d'en tester le\n    fonctionnement afin de déterminer les idées et principes qui sont à\n    la base de n'importe quel élément de ce Logiciel; et ceci, lorsque\n    le Licencié effectue toute opération de chargement, d'affichage,\n    d'exécution, de transmission ou de stockage du Logiciel qu'il est en\n    droit d'effectuer en vertu du Contrat.\n\n\n      5.2 DROIT D'APPORTER DES CONTRIBUTIONS\n\nLe droit d'apporter des Contributions comporte le droit de traduire,\nd'adapter, d'arranger ou d'apporter toute autre modification au Logiciel\net le droit de reproduire le logiciel en résultant.\n\nLe Licencié est autorisé à apporter toute Contribution au Logiciel sous\nréserve de mentionner, de façon explicite, son nom en tant qu'auteur de\ncette Contribution et la date de création de celle-ci.\n\n\n      5.3 DROIT DE DISTRIBUTION\n\nLe droit de distribution comporte notamment le droit de diffuser, de\ntransmettre et de communiquer le Logiciel au public sur tout support et\npar tout moyen ainsi que le droit de mettre sur le marché à titre\nonéreux ou gratuit, un ou des exemplaires du Logiciel par tout procédé.\n\nLe Licencié est autorisé à distribuer des copies du Logiciel, modifié ou\nnon, à des tiers dans les conditions ci-après détaillées.\n\n\n        5.3.1 DISTRIBUTION DU LOGICIEL SANS MODIFICATION\n\nLe Licencié est autorisé à distribuer des copies conformes du Logiciel,\nsous forme de Code Source ou de Code Objet, à condition que cette\ndistribution respecte les dispositions du Contrat dans leur totalité et\nsoit accompagnée:\n\n 1.\n\n    d'un exemplaire du Contrat,\n\n 2.\n\n    d'un avertissement relatif à la restriction de garantie et de\n    responsabilité du Concédant telle que prévue aux articles 8\n    <#responsabilite> et 9 <#garantie>,\n\net que, dans le cas où seul le Code Objet du Logiciel est redistribué,\nle Licencié permette un accès effectif au Code Source complet du\nLogiciel pour une durée d'au moins 3 ans à compter de la distribution du\nlogiciel, étant entendu que le coût additionnel d'acquisition du Code\nSource ne devra pas excéder le simple coût de transfert des données.\n\n\n        5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE\n\nLorsque le Licencié apporte une Contribution au Logiciel, les conditions\nde distribution du Logiciel Modifié en résultant sont alors soumises à\nl'intégralité des dispositions du Contrat.\n\nLe Licencié est autorisé à distribuer le Logiciel Modifié, sous forme de\ncode source ou de code objet, à condition que cette distribution\nrespecte les dispositions du Contrat dans leur totalité et soit\naccompagnée:\n\n 1.\n\n    d'un exemplaire du Contrat,\n\n 2.\n\n    d'un avertissement relatif à la restriction de garantie et de\n    responsabilité du Concédant telle que prévue aux articles 8\n    <#responsabilite> et 9 <#garantie>,\n\net, dans le cas où seul le code objet du Logiciel Modifié est redistribué,\n\n 3.\n\n    d'une note précisant les conditions d'accès effectif au code source\n    complet du Logiciel Modifié, pendant une période d'au moins 3 ans à\n    compter de la distribution du Logiciel Modifié, étant entendu que le\n    coût additionnel d'acquisition du code source ne devra pas excéder\n    le simple coût de transfert des données.\n\n\n        5.3.3 DISTRIBUTION DES MODULES EXTERNES\n\nLorsque le Licencié a développé un Module Externe les conditions du\nContrat ne s'appliquent pas à ce Module Externe, qui peut être distribué\nsous un contrat de licence différent.\n\n\n        5.3.4 COMPATIBILITE AVEC D'AUTRES LICENCES\n\nLe Licencié peut inclure un code soumis aux dispositions d'une des\nversions de la licence GNU GPL, GNU Affero GPL et/ou EUPL dans le\nLogiciel modifié ou non et distribuer l'ensemble sous les conditions de\nla même version de la licence GNU GPL, GNU Affero GPL et/ou EUPL.\n\nLe Licencié peut inclure le Logiciel modifié ou non dans un code soumis\naux dispositions d'une des versions de la licence GNU GPL, GNU Affero\nGPL et/ou EUPL et distribuer l'ensemble sous les conditions de la même\nversion de la licence GNU GPL, GNU Affero GPL et/ou EUPL.\n\n\n    Article 6 - PROPRIETE INTELLECTUELLE\n\n\n      6.1 SUR LE LOGICIEL INITIAL\n\nLe Titulaire est détenteur des droits patrimoniaux sur le Logiciel\nInitial. Toute utilisation du Logiciel Initial est soumise au respect\ndes conditions dans lesquelles le Titulaire a choisi de diffuser son\noeuvre et nul autre n'a la faculté de modifier les conditions de\ndiffusion de ce Logiciel Initial.\n\nLe Titulaire s'engage à ce que le Logiciel Initial reste au moins régi\npar le Contrat et ce, pour la durée visée à l'article 4.2 <#duree>.\n\n\n      6.2 SUR LES CONTRIBUTIONS\n\nLe Licencié qui a développé une Contribution est titulaire sur celle-ci\ndes droits de propriété intellectuelle dans les conditions définies par\nla législation applicable.\n\n\n      6.3 SUR LES MODULES EXTERNES\n\nLe Licencié qui a développé un Module Externe est titulaire sur celui-ci\ndes droits de propriété intellectuelle dans les conditions définies par\nla législation applicable et reste libre du choix du contrat régissant\nsa diffusion.\n\n\n      6.4 DISPOSITIONS COMMUNES\n\nLe Licencié s'engage expressément:\n\n 1.\n\n    à ne pas supprimer ou modifier de quelque manière que ce soit les\n    mentions de propriété intellectuelle apposées sur le Logiciel;\n\n 2.\n\n    à reproduire à l'identique lesdites mentions de propriété\n    intellectuelle sur les copies du Logiciel modifié ou non.\n\nLe Licencié s'engage à ne pas porter atteinte, directement ou\nindirectement, aux droits de propriété intellectuelle du Titulaire et/ou\ndes Contributeurs sur le Logiciel et à prendre, le cas échéant, à\nl'égard de son personnel toutes les mesures nécessaires pour assurer le\nrespect des dits droits de propriété intellectuelle du Titulaire et/ou\ndes Contributeurs.\n\n\n    Article 7 - SERVICES ASSOCIES\n\n7.1 Le Contrat n'oblige en aucun cas le Concédant à la réalisation de\nprestations d'assistance technique ou de maintenance du Logiciel.\n\nCependant le Concédant reste libre de proposer ce type de services. Les\ntermes et conditions d'une telle assistance technique et/ou d'une telle\nmaintenance seront alors déterminés dans un acte séparé. Ces actes de\nmaintenance et/ou assistance technique n'engageront que la seule\nresponsabilité du Concédant qui les propose.\n\n7.2 De même, tout Concédant est libre de proposer, sous sa seule\nresponsabilité, à ses licenciés une garantie, qui n'engagera que lui,\nlors de la redistribution du Logiciel et/ou du Logiciel Modifié et ce,\ndans les conditions qu'il souhaite. Cette garantie et les modalités\nfinancières de son application feront l'objet d'un acte séparé entre le\nConcédant et le Licencié.\n\n\n    Article 8 - RESPONSABILITE\n\n8.1 Sous réserve des dispositions de l'article 8.2\n<#limite-responsabilite>, le Licencié a la faculté, sous réserve de\nprouver la faute du Concédant concerné, de solliciter la réparation du\npréjudice direct qu'il subirait du fait du Logiciel et dont il apportera\nla preuve.\n\n8.2 La responsabilité du Concédant est limitée aux engagements pris en\napplication du Contrat et ne saurait être engagée en raison notamment:\n(i) des dommages dus à l'inexécution, totale ou partielle, de ses\nobligations par le Licencié, (ii) des dommages directs ou indirects\ndécoulant de l'utilisation ou des performances du Logiciel subis par le\nLicencié et (iii) plus généralement d'un quelconque dommage indirect. En\nparticulier, les Parties conviennent expressément que tout préjudice\nfinancier ou commercial (par exemple perte de données, perte de\nbénéfices, perte d'exploitation, perte de clientèle ou de commandes,\nmanque à gagner, trouble commercial quelconque) ou toute action dirigée\ncontre le Licencié par un tiers, constitue un dommage indirect et\nn'ouvre pas droit à réparation par le Concédant.\n\n\n    Article 9 - GARANTIE\n\n9.1 Le Licencié reconnaît que l'état actuel des connaissances\nscientifiques et techniques au moment de la mise en circulation du\nLogiciel ne permet pas d'en tester et d'en vérifier toutes les\nutilisations ni de détecter l'existence d'éventuels défauts. L'attention\ndu Licencié a été attirée sur ce point sur les risques associés au\nchargement, à l'utilisation, la modification et/ou au développement et à\nla reproduction du Logiciel qui sont réservés à des utilisateurs avertis.\n\nIl relève de la responsabilité du Licencié de contrôler, par tous\nmoyens, l'adéquation du produit à ses besoins, son bon fonctionnement et\nde s'assurer qu'il ne causera pas de dommages aux personnes et aux biens.\n\n9.2 Le Concédant déclare de bonne foi être en droit de concéder\nl'ensemble des droits attachés au Logiciel (comprenant notamment les\ndroits visés à l'article 5 <#etendue>).\n\n9.3 Le Licencié reconnaît que le Logiciel est fourni \"en l'état\" par le\nConcédant sans autre garantie, expresse ou tacite, que celle prévue à\nl'article 9.2 <#bonne-foi> et notamment sans aucune garantie sur sa\nvaleur commerciale, son caractère sécurisé, innovant ou pertinent.\n\nEn particulier, le Concédant ne garantit pas que le Logiciel est exempt\nd'erreur, qu'il fonctionnera sans interruption, qu'il sera compatible\navec l'équipement du Licencié et sa configuration logicielle ni qu'il\nremplira les besoins du Licencié.\n\n9.4 Le Concédant ne garantit pas, de manière expresse ou tacite, que le\nLogiciel ne porte pas atteinte à un quelconque droit de propriété\nintellectuelle d'un tiers portant sur un brevet, un logiciel ou sur tout\nautre droit de propriété. Ainsi, le Concédant exclut toute garantie au\nprofit du Licencié contre les actions en contrefaçon qui pourraient être\ndiligentées au titre de l'utilisation, de la modification, et de la\nredistribution du Logiciel. Néanmoins, si de telles actions sont\nexercées contre le Licencié, le Concédant lui apportera son expertise\ntechnique et juridique pour sa défense. Cette expertise technique et\njuridique est déterminée au cas par cas entre le Concédant concerné et\nle Licencié dans le cadre d'un protocole d'accord. Le Concédant dégage\ntoute responsabilité quant à l'utilisation de la dénomination du\nLogiciel par le Licencié. Aucune garantie n'est apportée quant à\nl'existence de droits antérieurs sur le nom du Logiciel et sur\nl'existence d'une marque.\n\n\n    Article 10 - RESILIATION\n\n10.1 En cas de manquement par le Licencié aux obligations mises à sa\ncharge par le Contrat, le Concédant pourra résilier de plein droit le\nContrat trente (30) jours après notification adressée au Licencié et\nrestée sans effet.\n\n10.2 Le Licencié dont le Contrat est résilié n'est plus autorisé à\nutiliser, modifier ou distribuer le Logiciel. Cependant, toutes les\nlicences qu'il aura concédées antérieurement à la résiliation du Contrat\nresteront valides sous réserve qu'elles aient été effectuées en\nconformité avec le Contrat.\n\n\n    Article 11 - DISPOSITIONS DIVERSES\n\n\n      11.1 CAUSE EXTERIEURE\n\nAucune des Parties ne sera responsable d'un retard ou d'une défaillance\nd'exécution du Contrat qui serait dû à un cas de force majeure, un cas\nfortuit ou une cause extérieure, telle que, notamment, le mauvais\nfonctionnement ou les interruptions du réseau électrique ou de\ntélécommunication, la paralysie du réseau liée à une attaque\ninformatique, l'intervention des autorités gouvernementales, les\ncatastrophes naturelles, les dégâts des eaux, les tremblements de terre,\nle feu, les explosions, les grèves et les conflits sociaux, l'état de\nguerre...\n\n11.2 Le fait, par l'une ou l'autre des Parties, d'omettre en une ou\nplusieurs occasions de se prévaloir d'une ou plusieurs dispositions du\nContrat, ne pourra en aucun cas impliquer renonciation par la Partie\nintéressée à s'en prévaloir ultérieurement.\n\n11.3 Le Contrat annule et remplace toute convention antérieure, écrite\nou orale, entre les Parties sur le même objet et constitue l'accord\nentier entre les Parties sur cet objet. Aucune addition ou modification\naux termes du Contrat n'aura d'effet à l'égard des Parties à moins\nd'être faite par écrit et signée par leurs représentants dûment habilités.\n\n11.4 Dans l'hypothèse où une ou plusieurs des dispositions du Contrat\ns'avèrerait contraire à une loi ou à un texte applicable, existants ou\nfuturs, cette loi ou ce texte prévaudrait, et les Parties feraient les\namendements nécessaires pour se conformer à cette loi ou à ce texte.\nToutes les autres dispositions resteront en vigueur. De même, la\nnullité, pour quelque raison que ce soit, d'une des dispositions du\nContrat ne saurait entraîner la nullité de l'ensemble du Contrat.\n\n\n      11.5 LANGUE\n\nLe Contrat est rédigé en langue française et en langue anglaise, ces\ndeux versions faisant également foi.\n\n\n    Article 12 - NOUVELLES VERSIONS DU CONTRAT\n\n12.1 Toute personne est autorisée à copier et distribuer des copies de\nce Contrat.\n\n12.2 Afin d'en préserver la cohérence, le texte du Contrat est protégé\net ne peut être modifié que par les auteurs de la licence, lesquels se\nréservent le droit de publier périodiquement des mises à jour ou de\nnouvelles versions du Contrat, qui posséderont chacune un numéro\ndistinct. Ces versions ultérieures seront susceptibles de prendre en\ncompte de nouvelles problématiques rencontrées par les logiciels libres.\n\n12.3 Tout Logiciel diffusé sous une version donnée du Contrat ne pourra\nfaire l'objet d'une diffusion ultérieure que sous la même version du\nContrat ou une version postérieure, sous réserve des dispositions de\nl'article 5.3.4 <#compatibilite>.\n\n\n    Article 13 - LOI APPLICABLE ET COMPETENCE TERRITORIALE\n\n13.1 Le Contrat est régi par la loi française. Les Parties conviennent\nde tenter de régler à l'amiable les différends ou litiges qui\nviendraient à se produire par suite ou à l'occasion du Contrat.\n\n13.2 A défaut d'accord amiable dans un délai de deux (2) mois à compter\nde leur survenance et sauf situation relevant d'une procédure d'urgence,\nles différends ou litiges seront portés par la Partie la plus diligente\ndevant les Tribunaux compétents de Paris.\n\n\n"
  },
  {
    "path": "README.md",
    "content": "# BMC-Tools\nRDP Bitmap Cache parser.\n## Input\n`bmc-tools` processes `bcache*.bmc` and `cache????.bin` files found inside Windows user profiles.\n## Usage\n```sh\n./bmc-tools.py [-h] -s SRC -d DEST [-c COUNT] [-v] [-o] [-b] [-w WIDTH]\n```\nWith the following arguments meaning:\n```\n  -h, --help              show this help message and exit\n  -s SRC, --src SRC       Specify the BMCache file or directory to process.\n  -d DEST, --dest DEST    Specify the directory where to store the extracted bitmaps.\n  -c COUNT, --count COUNT Only extract the given number of bitmaps.\n  -v, --verbose           Determine the amount of information displayed.\n  -o, --old               Extract the old bitmap data found in the BMCache file.\n  -b, --bitmap            Provide a collage bitmap aggregating all the tiles.\n  -w WIDTH, --width WIDTH Specify the number of tiles per line of the aggregated bitmap (default=64).\n  -k, --kape            Use this option to split out the different inputs into separate folders\n```\n## Changelog\n```\n01/12/2023\t\t3.04  Fix memory usage for huge speed improvement\n15/05/2023\t\t3.03  Added KAPE output to split output into seperate folders\n02/03/2023\t\t3.02\tAdded destination folder existence check beforehand.\n01/03/2023\t\t3.01\tFixed old Bitmaps storage and export.\n10/02/2022\t\t3.00\tNow performing tile decompression.\n07/12/2020\t\t2.11\tCorrected minor string printing issue under Python3.\n07/12/2020\t\t2.10\tImproved collage creation under Python3.\n04/12/2020\t\t2.00\tNow compatible with both Python2 and Python3.\n23/11/2020\t\t1.04\tFixed Bitmap size field.\n30/04/2018\t\t1.03\tAdded extra aggregated bitmap/collage output.\n22/04/2018\t\t1.02\tAdded support for (old?) bcache23.bmc files.\n25/11/2016\t\t1.01\tCompressed data handling improved.\n25/11/2016\t\t1.00c\tUnused variable removed.\n10/08/2016\t\t1.00b\t--dest parameter processing fixed.\n01/07/2016\t\t1.00a\tcacheXXXX.bin header detection fixed.\n27/06/2016\t\t1.00\tInitial release.\n```\n"
  },
  {
    "path": "bmc-tools.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport argparse, os, os.path, sys\nfrom struct import pack, unpack\n\nclass BMCContainer():\n\tBIN_FILE_HEADER = b\"RDP8bmp\\x00\"\n\tBIN_CONTAINER = b\".BIN\"\n\tBMC_CONTAINER = b\".BMC\"\n\tTILE_HEADER_SIZE = {BMC_CONTAINER: 0x14, BIN_CONTAINER: 0xC}\n\tSTRIPE_WIDTH = 64\n\tLOG_TYPES = [\"[===]\", \"[+++]\", \"[---]\", \"[!!!]\"]\n\tPALETTE = bytes(bytearray((0, 0, 0, 0, 0, 0, 128, 0, 0, 128, 0, 0, 0, 128, 128, 0, 128, 0, 0, 0, 128, 0, 128, 0, 128, 128, 0, 0, 192, 192, 192, 0, 192, 220, 192, 0, 240, 202, 166, 0, 0, 32, 64, 0, 0, 32, 96, 0, 0, 32, 128, 0, 0, 32, 160, 0, 0, 32, 192, 0, 0, 32, 224, 0, 0, 64, 0, 0, 0, 64, 32, 0, 0, 64, 64, 0, 0, 64, 96, 0, 0, 64, 128, 0, 0, 64, 160, 0, 0, 64, 192, 0, 0, 64, 224, 0, 0, 96, 0, 0, 0, 96, 32, 0, 0, 96, 64, 0, 0, 96, 96, 0, 0, 96, 128, 0, 0, 96, 160, 0, 0, 96, 192, 0, 0, 96, 224, 0, 0, 128, 0, 0, 0, 128, 32, 0, 0, 128, 64, 0, 0, 128, 96, 0, 0, 128, 128, 0, 0, 128, 160, 0, 0, 128, 192, 0, 0, 128, 224, 0, 0, 160, 0, 0, 0, 160, 32, 0, 0, 160, 64, 0, 0, 160, 96, 0, 0, 160, 128, 0, 0, 160, 160, 0, 0, 160, 192, 0, 0, 160, 224, 0, 0, 192, 0, 0, 0, 192, 32, 0, 0, 192, 64, 0, 0, 192, 96, 0, 0, 192, 128, 0, 0, 192, 160, 0, 0, 192, 192, 0, 0, 192, 224, 0, 0, 224, 0, 0, 0, 224, 32, 0, 0, 224, 64, 0, 0, 224, 96, 0, 0, 224, 128, 0, 0, 224, 160, 0, 0, 224, 192, 0, 0, 224, 224, 0, 64, 0, 0, 0, 64, 0, 32, 0, 64, 0, 64, 0, 64, 0, 96, 0, 64, 0, 128, 0, 64, 0, 160, 0, 64, 0, 192, 0, 64, 0, 224, 0, 64, 32, 0, 0, 64, 32, 32, 0, 64, 32, 64, 0, 64, 32, 96, 0, 64, 32, 128, 0, 64, 32, 160, 0, 64, 32, 192, 0, 64, 32, 224, 0, 64, 64, 0, 0, 64, 64, 32, 0, 64, 64, 64, 0, 64, 64, 96, 0, 64, 64, 128, 0, 64, 64, 160, 0, 64, 64, 192, 0, 64, 64, 224, 0, 64, 96, 0, 0, 64, 96, 32, 0, 64, 96, 64, 0, 64, 96, 96, 0, 64, 96, 128, 0, 64, 96, 160, 0, 64, 96, 192, 0, 64, 96, 224, 0, 64, 128, 0, 0, 64, 128, 32, 0, 64, 128, 64, 0, 64, 128, 96, 0, 64, 128, 128, 0, 64, 128, 160, 0, 64, 128, 192, 0, 64, 128, 224, 0, 64, 160, 0, 0, 64, 160, 32, 0, 64, 160, 64, 0, 64, 160, 96, 0, 64, 160, 128, 0, 64, 160, 160, 0, 64, 160, 192, 0, 64, 160, 224, 0, 64, 192, 0, 0, 64, 192, 32, 0, 64, 192, 64, 0, 64, 192, 96, 0, 64, 192, 128, 0, 64, 192, 160, 0, 64, 192, 192, 0, 64, 192, 224, 0, 64, 224, 0, 0, 64, 224, 32, 0, 64, 224, 64, 0, 64, 224, 96, 0, 64, 224, 128, 0, 64, 224, 160, 0, 64, 224, 192, 0, 64, 224, 224, 0, 128, 0, 0, 0, 128, 0, 32, 0, 128, 0, 64, 0, 128, 0, 96, 0, 128, 0, 128, 0, 128, 0, 160, 0, 128, 0, 192, 0, 128, 0, 224, 0, 128, 32, 0, 0, 128, 32, 32, 0, 128, 32, 64, 0, 128, 32, 96, 0, 128, 32, 128, 0, 128, 32, 160, 0, 128, 32, 192, 0, 128, 32, 224, 0, 128, 64, 0, 0, 128, 64, 32, 0, 128, 64, 64, 0, 128, 64, 96, 0, 128, 64, 128, 0, 128, 64, 160, 0, 128, 64, 192, 0, 128, 64, 224, 0, 128, 96, 0, 0, 128, 96, 32, 0, 128, 96, 64, 0, 128, 96, 96, 0, 128, 96, 128, 0, 128, 96, 160, 0, 128, 96, 192, 0, 128, 96, 224, 0, 128, 128, 0, 0, 128, 128, 32, 0, 128, 128, 64, 0, 128, 128, 96, 0, 128, 128, 128, 0, 128, 128, 160, 0, 128, 128, 192, 0, 128, 128, 224, 0, 128, 160, 0, 0, 128, 160, 32, 0, 128, 160, 64, 0, 128, 160, 96, 0, 128, 160, 128, 0, 128, 160, 160, 0, 128, 160, 192, 0, 128, 160, 224, 0, 128, 192, 0, 0, 128, 192, 32, 0, 128, 192, 64, 0, 128, 192, 96, 0, 128, 192, 128, 0, 128, 192, 160, 0, 128, 192, 192, 0, 128, 192, 224, 0, 128, 224, 0, 0, 128, 224, 32, 0, 128, 224, 64, 0, 128, 224, 96, 0, 128, 224, 128, 0, 128, 224, 160, 0, 128, 224, 192, 0, 128, 224, 224, 0, 192, 0, 0, 0, 192, 0, 32, 0, 192, 0, 64, 0, 192, 0, 96, 0, 192, 0, 128, 0, 192, 0, 160, 0, 192, 0, 192, 0, 192, 0, 224, 0, 192, 32, 0, 0, 192, 32, 32, 0, 192, 32, 64, 0, 192, 32, 96, 0, 192, 32, 128, 0, 192, 32, 160, 0, 192, 32, 192, 0, 192, 32, 224, 0, 192, 64, 0, 0, 192, 64, 32, 0, 192, 64, 64, 0, 192, 64, 96, 0, 192, 64, 128, 0, 192, 64, 160, 0, 192, 64, 192, 0, 192, 64, 224, 0, 192, 96, 0, 0, 192, 96, 32, 0, 192, 96, 64, 0, 192, 96, 96, 0, 192, 96, 128, 0, 192, 96, 160, 0, 192, 96, 192, 0, 192, 96, 224, 0, 192, 128, 0, 0, 192, 128, 32, 0, 192, 128, 64, 0, 192, 128, 96, 0, 192, 128, 128, 0, 192, 128, 160, 0, 192, 128, 192, 0, 192, 128, 224, 0, 192, 160, 0, 0, 192, 160, 32, 0, 192, 160, 64, 0, 192, 160, 96, 0, 192, 160, 128, 0, 192, 160, 160, 0, 192, 160, 192, 0, 192, 160, 224, 0, 192, 192, 0, 0, 192, 192, 32, 0, 192, 192, 64, 0, 192, 192, 96, 0, 192, 192, 128, 0, 192, 192, 160, 0, 240, 251, 255, 0, 164, 160, 160, 0, 128, 128, 128, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 255, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255, 255, 0, 0, 255, 255, 255, 0)))\n\tCOLOR_BLACK = b\"\\x00\"\n\tCOLOR_WHITE = b\"\\xFF\"\n\tdef __init__(self, verbose=False, count=0, old=False, big=False, width=64):\n\t\tself.bdat = \"\"\n\t\tself.o_bmps = []\n\t\tself.bmps = []\n\t\tself.btype = None\n\t\tself.cnt = count\n\t\tself.fname = None\n\t\tself.oldsave = old\n\t\tself.pal = False\n\t\tself.verb = verbose\n\t\tself.big = big\n\t\tself.STRIPE_WIDTH = width\n\t\tif count > 0:\n\t\t\tself.b_log(sys.stdout, True, 2, \"At most %d tiles will be processed.\" % (count))\n\t\tif old:\n\t\t\tself.b_log(sys.stdout, True, 2, \"Old data will also be saved in separate files.\")\n\tdef b_log(self, stream, verbose, ltype, lmsg):\n\t\tif not verbose or self.verb:\n\t\t\tstream.write(\"%s %s%s\" % (self.LOG_TYPES[ltype], lmsg, os.linesep))\n\t\treturn True\n\tdef b_import(self, fname):\n\t\tif len(self.bdat) > 0:\n\t\t\tself.b_log(sys.stderr, False, 3, \"Data is already waiting to be processed; aborting.\")\n\t\t\treturn False\n\t\twith open(fname, \"rb\") as f:\n\t\t\tself.bdat = memoryview(f.read())\n\t\tif len(self.bdat) == 0:\n\t\t\tself.b_log(sys.stderr, False, 3, \"Unable to retrieve file contents; aborting.\")\n\t\t\treturn False\n\t\tself.fname = fname\n\t\tself.btype = self.BMC_CONTAINER\n\t\tif self.bdat[:len(self.BIN_FILE_HEADER)] == self.BIN_FILE_HEADER:\n\t\t\tself.b_log(sys.stdout, True, 2, \"Subsequent header version: %d.\" % (unpack(\"<L\", self.bdat[len(self.BIN_FILE_HEADER):len(self.BIN_FILE_HEADER)+4])[0]))\n\t\t\tself.bdat = self.bdat[len(self.BIN_FILE_HEADER)+4:]\n\t\t\tself.btype = self.BIN_CONTAINER\n\t\tself.b_log(sys.stdout, True, 0, \"Successfully loaded '%s' as a %s container.\" % (self.fname, self.btype.decode()))\n\t\treturn True\n\tdef b_process(self):\n\t\tif len(self.bdat) == 0:\n\t\t\tself.b_log(sys.stderr, False, 3, \"Nothing to process.\")\n\t\t\treturn False\n\t\tbl = 0\n\t\twhile len(self.bdat) > 0:\n\t\t\told = False\n\t\t\to_bmp = \"\"\n\t\t\tt_hdr = self.bdat[:self.TILE_HEADER_SIZE[self.btype]]\n\t\t\tkey1, key2, t_width, t_height = unpack(\"<LLHH\", t_hdr[:0xC])\n\t\t\tif self.btype == self.BIN_CONTAINER:\n\t\t\t\tbl = 4*t_width*t_height\n\t\t\t\tt_bmp = self.b_parse_rgb32b(self.bdat[len(t_hdr):len(t_hdr)+bl].tobytes())\n\t\t\telif self.btype == self.BMC_CONTAINER:\n\t\t\t\tt_bmp = \"\"\n\t\t\t\tt_len, t_params = unpack(\"<LL\", t_hdr[-0x8:])\n\t\t\t\tif t_params & 0x08: #This bit is always ONE when relevant data is smaller than expected data, thus it is most likely the \"compression\" bit flag.\n\t\t\t\t\tif bl == 0:\n\t\t\t\t\t\tif \"22.bmc\" in self.fname:\n\t\t\t\t\t\t\tbl = 64*64*2\n\t\t\t\t\t\telif \"24.bmc\" in self.fname:\n\t\t\t\t\t\t\tbl = 64*64*4\n\t\t\t\t\t\telif \"2.bmc\" in self.fname:\n\t\t\t\t\t\t\tbl = 64*64\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\tfor b in [1, 2, 4]:\n\t\t\t\t\t\t\t\tif len(self.bdat) < len(t_hdr)+64*64*b+8:\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\telif unpack(\"<H\", self.bdat[len(t_hdr)+64*64*b+8:][:2])[0] == 64:\n\t\t\t\t\t\t\t\t\tbl = 64*64*b\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tif bl == 0:\n\t\t\t\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unable to determine data pattern size; exiting before throwing any error!\")\n\t\t\t\t\t\t\t\treturn False\n\t\t\t\t\to_bmp = b\"\"\n\t\t\t\t\tt_bmp = self.b_uncompress(self.bdat[len(t_hdr):len(t_hdr)+t_len].tobytes(), bl//(64*64))\n\t\t\t\t\tif len(t_bmp) > 0:\n\t\t\t\t\t\tif len(t_bmp) != t_width*t_height*bl//(64*64):\n\t\t\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Uncompressed tile data seems bogus (uncompressed %d bytes while expecting %d). Discarding tile.\" % (len(t_bmp), t_width*t_height*bl//(64*64)))\n\t\t\t\t\t\t\tt_bmp = b\"\"\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\tt_bmp = self.b_parse_rgb565(t_bmp)\n\t\t\t\telse:\n\t\t\t\t\tcf = t_len//(t_width*t_height)\n\t\t\t\t\tif cf == 4:\n\t\t\t\t\t\tt_bmp = self.b_parse_rgb32b(self.bdat[len(t_hdr):len(t_hdr)+cf*t_width*t_height].tobytes())\n\t\t\t\t\t\tif t_height != 64:\n\t\t\t\t\t\t\told = True\n\t\t\t\t\t\t\to_bmp = self.b_parse_rgb32b(self.bdat[len(t_hdr)+cf*t_width*t_height:len(t_hdr)+cf*64*64].tobytes())\n\t\t\t\t\telif cf == 3:\n\t\t\t\t\t\tt_bmp = self.b_parse_rgb24b(self.bdat[len(t_hdr):len(t_hdr)+cf*t_width*t_height].tobytes())\n\t\t\t\t\t\tif t_height != 64:\n\t\t\t\t\t\t\told = True\n\t\t\t\t\t\t\to_bmp = self.b_parse_rgb24b(self.bdat[len(t_hdr)+cf*t_width*t_height:len(t_hdr)+cf*64*64].tobytes())\n\t\t\t\t\telif cf == 2:\n\t\t\t\t\t\tt_bmp = self.b_parse_rgb565(self.bdat[len(t_hdr):len(t_hdr)+cf*t_width*t_height].tobytes())\n\t\t\t\t\t\tif t_height != 64:\n\t\t\t\t\t\t\told = True\n\t\t\t\t\t\t\to_bmp = self.b_parse_rgb565(self.bdat[len(t_hdr)+cf*t_width*t_height:len(t_hdr)+cf*64*64].tobytes())\n\t\t\t\t\telif cf == 1:\n\t\t\t\t\t\tself.pal = True\n\t\t\t\t\t\tt_bmp = self.PALETTE+self.bdat[len(t_hdr):len(t_hdr)+cf*t_width*t_height].tobytes()\n\t\t\t\t\t\tif t_height != 64:\n\t\t\t\t\t\t\told = True\n\t\t\t\t\t\t\to_bmp = self.PALETTE+self.bdat[len(t_hdr)+cf*t_width*t_height:len(t_hdr)+cf*64*64].tobytes()\n\t\t\t\t\telse:\n\t\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected bpp (%d) found during processing; aborting.\" % (8*cf))\n\t\t\t\t\t\treturn False\n\t\t\t\t\tbl = cf*64*64\n\t\t\tif len(t_bmp) > 0:\n\t\t\t\tself.bmps.append(t_bmp)\n\t\t\t\tself.o_bmps.append(o_bmp)\n\t\t\t\tif len(self.bmps)%100 == 0:\n\t\t\t\t\tself.b_log(sys.stdout, True, 1, \"%d tiles successfully extracted so far.\" % (len(self.bmps)))\n\t\t\tself.bdat = self.bdat[len(t_hdr)+bl:]\n\t\t\tif self.cnt != 0 and len(self.bmps) == self.cnt:\n\t\t\t\tbreak\n\t\tself.b_log(sys.stdout, False, 0, \"%d tiles successfully extracted in the end.\" % (len(self.bmps)))\n\t\treturn True\n\tdef b_parse_rgb565(self, data):\n\t\td_out = b\"\"\n\t\twhile len(data) > 0:\n\t\t\tpxl = unpack(\"<H\", data[:2])[0]\n\t\t\tbl = ((pxl>>8)&0xF8)|((pxl>>13)&0x07)\n\t\t\tgr = ((pxl>>3)&0xFC)|((pxl>>9)&0x03)\n\t\t\tre = ((pxl<<3)&0xF8)|((pxl>>2)&0x07)\n\t\t\td_out+=bytearray((re, gr, bl, 255))\n\t\t\tdata = data[2:]\n\t\treturn bytes(d_out)\n\tdef b_parse_rgb32b(self, data):\n\t\td_out = b\"\"\n\t\td_buf = b\"\"\n\t\twhile len(data) > 0:\n\t\t\tif self.btype == self.BIN_CONTAINER:\n\t\t\t\td_buf+=data[:3]+b\"\\xFF\"\n\t\t\t\tif len(d_buf) == 256:\n\t\t\t\t\td_out = d_buf+d_out\n\t\t\t\t\td_buf = b\"\"\n\t\t\telse:\n\t\t\t\td_out+=data[:3]+b\"\\xFF\"\n\t\t\tdata = data[4:]\n\t\treturn d_out\n\tdef b_parse_rgb24b(self, data):\n\t\td_out = b\"\"\n\t\td_buf = b\"\"\n\t\twhile len(data) > 0:\n\t\t\tif self.btype == self.BIN_CONTAINER:\n\t\t\t\td_buf+=data[:3]+b\"\\xFF\"\n\t\t\t\tif len(d_buf) == 256:\n\t\t\t\t\td_out = d_buf+d_out\n\t\t\t\t\td_buf = b\"\"\n\t\t\telse:\n\t\t\t\td_out+=data[:3]+b\"\\xFF\"\n\t\t\tdata = data[3:]\n\t\treturn d_out\n\tdef b_unrle(self, data):\n\t\tif len(data) == 0:\n\t\t\treturn (-1, 1, 0)\n\t\tx = ord(data[0:1])\n\t\tif (x&0xF0) == 0xF0:\n\t\t\tif x in [0xF5, 0xFB, 0xFC, 0xFF]:\n\t\t\t\treturn (-1, 2, x)\n\t\t\telif x in [0xFD, 0xFE]:\n\t\t\t\treturn (x, 0, 1)\n\t\t\telif x in [0xF9, 0xFA]:\n\t\t\t\treturn (x, 8, 1)\n\t\t\telif len(data) < 3:\n\t\t\t\treturn (-1, 1, 0)\n\t\t\telse:\n\t\t\t\tc = unpack(\"<H\", data[1:3])[0]\n\t\t\t\treturn (x, c, 3)\n\t\telif (x&0xE0) == 0xA0:\n\t\t\treturn (-1, 2, x)\n\t\telse:\n\t\t\tif (x&0x80) == 0x00 or (x&0xE0) == 0x80:\n\t\t\t\tc = x&0x1F\n\t\t\t\tx = x&0xE0\n\t\t\t\to = 32\n\t\t\telse:\n\t\t\t\tc = x&0x0F\n\t\t\t\tx = x&0xF0\n\t\t\t\to = 16\n\t\t\tif x in [0x40, 0xD0]:\n\t\t\t\tc*=8\n\t\t\t\to = 1\n\t\t\tif c == 0:\n\t\t\t\tif len(data) < 2:\n\t\t\t\t\treturn (-1, 1, 0)\n\t\t\t\telse:\n\t\t\t\t\tc = ord(data[1:2])+o\n\t\t\t\to = 2\n\t\t\telse:\n\t\t\t\to = 1\n\t\t\treturn (x, c, o)\n\t\treturn (-1, 3, 0)\n\tdef b_uncompress(self, data, bbp):\n\t\td_out = b\"\"\n\t\tbro = -1\n\t\tfgc = self.COLOR_WHITE*bbp\n\t\twhile len(data) > 0:\n\t\t\tcmd, rl, sz = self.b_unrle(data[:3])\n\t\t\tif cmd == -1:\n\t\t\t\tif rl == 1:\n\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\")\n\t\t\t\telif rl == 2:\n\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected decompression command encountered (0x%02X). Skipping tile.\" % (sz))\n\t\t\t\telse:\n\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unhandled case in decompression routine. Skipping tile.\")\n\t\t\t\treturn b\"\"\n\t\t\tdata = data[sz:]\n\t\t\tif cmd in [0x00, 0xF0]:\n\t\t\t\tif len(d_out) < 64*bbp:\n\t\t\t\t\tif bro == 0:\n\t\t\t\t\t\td_out+=fgc\n\t\t\t\t\t\trl-=1\n\t\t\t\t\td_out+=(self.COLOR_BLACK*bbp)*rl\n\t\t\t\telse:\n\t\t\t\t\tif bro > 0:\n\t\t\t\t\t\tc = d_out[-64*bbp:][:bbp]\n\t\t\t\t\t\tfor i in range(bbp):\n\t\t\t\t\t\t\td_out+=bytearray((ord(c[i:i+1])^ord(fgc[i:i+1]), ))\n\t\t\t\t\t\trl-=1\n\t\t\t\t\twhile rl > 0:\n\t\t\t\t\t\td_out+=d_out[-64*bbp:][:bbp]\n\t\t\t\t\t\trl-=1\n\t\t\t\tbro = len(d_out)//(64*bbp)\n\t\t\telif cmd in [0x20, 0xC0, 0xF1, 0xF6]:\n\t\t\t\tif cmd in [0xC0, 0xF6]:\n\t\t\t\t\tif len(data) < bbp:\n\t\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\" % (cmd, rl))\n\t\t\t\t\t\treturn b\"\"\n\t\t\t\t\tfgc = data[:bbp]\n\t\t\t\t\tdata = data[bbp:]\n\t\t\t\tif len(d_out) < 64*bbp:\n\t\t\t\t\td_out+=fgc*rl\n\t\t\t\telse:\n\t\t\t\t\twhile rl > 0:\n\t\t\t\t\t\tc = d_out[-64*bbp:][:bbp]\n\t\t\t\t\t\tfor i in range(bbp):\n\t\t\t\t\t\t\td_out+=bytearray((ord(c[i:i+1])^ord(fgc[i:i+1]), ))\n\t\t\t\t\t\trl-=1\n\t\t\telif cmd in [0xE0, 0xF8]:\n\t\t\t\tif len(data) < 2*bbp:\n\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\")\n\t\t\t\t\treturn b\"\"\n\t\t\t\td_out+=data[:2*bbp]*rl\n\t\t\t\tdata = data[2*bbp:]\n\t\t\telif cmd in [0x60, 0xF3]:\n\t\t\t\tif len(data) < bbp:\n\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\")\n\t\t\t\t\treturn b\"\"\n\t\t\t\td_out+=data[:bbp]*rl\n\t\t\t\tdata = data[bbp:]\n\t\t\telif cmd in [0x40, 0xD0, 0xF2, 0xF7, 0xF9, 0xFA]:\n\t\t\t\tif cmd in [0xD0, 0xF7]:\n\t\t\t\t\tif len(data) < bbp:\n\t\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\")\n\t\t\t\t\t\treturn b\"\"\n\t\t\t\t\tfgc = data[:bbp]\n\t\t\t\t\tdata = data[bbp:]\n\t\t\t\tif cmd == 0xF9:\n\t\t\t\t\tmsk = b\"\\x03\"\n\t\t\t\t\tml = 1\n\t\t\t\telif cmd == 0xFA:\n\t\t\t\t\tmsk = b\"\\x05\"\n\t\t\t\t\tml = 1\n\t\t\t\telse:\n\t\t\t\t\tif (rl%8) != 0:\n\t\t\t\t\t\tml = (rl//8)+1\n\t\t\t\t\telse:\n\t\t\t\t\t\tml = rl//8\n\t\t\t\t\tif len(data) < ml:\n\t\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\")\n\t\t\t\t\t\treturn b\"\"\n\t\t\t\t\tmsk = data[:ml]\n\t\t\t\t\tdata = data[ml:]\n\t\t\t\tk = 0\n\t\t\t\twhile rl > 0:\n\t\t\t\t\tif (k%8) == 0:\n\t\t\t\t\t\tm = ord(msk[k//8:][:1])\n\t\t\t\t\tb = m&(0x1<<(k%8))\n\t\t\t\t\tif len(d_out) < 64*bbp:\n\t\t\t\t\t\tif b == 0:\n\t\t\t\t\t\t\td_out+=(self.COLOR_BLACK*bbp)\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\td_out+=fgc\n\t\t\t\t\telse:\n\t\t\t\t\t\tc = d_out[-64*bbp:][:bbp]\n\t\t\t\t\t\tif b == 0:\n\t\t\t\t\t\t\td_out+=c\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\tfor i in range(bbp):\n\t\t\t\t\t\t\t\td_out+=bytearray((ord(c[i:i+1])^ord(fgc[i:i+1]), ))\n\t\t\t\t\tk+=1\n\t\t\t\t\trl-=1\n\t\t\telif cmd in [0x80, 0xF4]:\n\t\t\t\tif len(data) < bbp*rl:\n\t\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unexpected end of compressed stream. Skipping tile.\")\n\t\t\t\t\treturn b\"\"\n\t\t\t\td_out+=data[:rl*bbp]\n\t\t\t\tdata = data[rl*bbp:]\n\t\t\telif cmd == 0xFD:\n\t\t\t\td_out+=(self.COLOR_WHITE*bbp)\n\t\t\telif cmd == 0xFE:\n\t\t\t\td_out+=(self.COLOR_BLACK*bbp)\n\t\t\telse:\n\t\t\t\tself.b_log(sys.stderr, False, 3, \"Unhandled decompression command (0x%02X). Skipping tile.\" % (cmd))\n\t\t\t\treturn b\"\"\n\t\t\tif cmd not in [0x00, 0xF0]:\n\t\t\t\tbro = -1\n\t\treturn d_out\n\tdef b_export(self, dname):\n\t\tif not os.path.isdir(dname):\n\t\t\tself.b_log(sys.stderr, False, 3, \"Destination must be an already existing folder.\")\n\t\t\treturn False\n\t\tself.fname = os.path.basename(self.fname)\n\t\tfor i in range(len(self.bmps)):\n\t\t\tself.b_write(os.path.join(dname, \"%s_%04d.bmp\" % (self.fname, i)), self.b_export_bmp(64, len(self.bmps[i])//256, self.bmps[i]))\n\t\t\tif self.oldsave and i < len(self.o_bmps) and len(self.o_bmps[i]) > 0:\n\t\t\t\tself.b_write(os.path.join(dname, \"%s_old_%04d.bmp\" % (self.fname, i)), self.b_export_bmp(64, len(self.o_bmps[i])//256, self.o_bmps[i]))\n\t\tself.b_log(sys.stdout, False, 0, \"Successfully exported %d files.\" % (len(self.bmps)))\n\t\tif self.big:\n\t\t\tpad = b\"\\xFF\"\n\t\t\tif not self.pal:\n\t\t\t\tpad*=4\n\t\t\tfor i in range(len(self.bmps)):\n\t\t\t\tif self.pal:\n\t\t\t\t\tself.bmps[i] = self.bmps[i][len(self.PALETTE):]\n\t\t\t\twhile len(self.bmps[i]) < 64*64*len(pad):\n\t\t\t\t\tself.bmps[i]+=pad*64\n\t\t\tw = 64*len(self.bmps)\n\t\t\th = 64\n\t\t\tif len(self.bmps)//self.STRIPE_WIDTH > 0:\n\t\t\t\tm = len(self.bmps)%self.STRIPE_WIDTH\n\t\t\t\tif m != 0:\n\t\t\t\t\tfor i in range(self.STRIPE_WIDTH-m):\n\t\t\t\t\t\tself.bmps.append(pad*64*64)\n\t\t\t\tw = self.STRIPE_WIDTH*64\n\t\t\t\th*=len(self.bmps)//self.STRIPE_WIDTH\n\t\t\tc_bmp = b\"\" if not self.pal else self.PALETTE\n\t\t\tif self.btype == self.BIN_CONTAINER:\n\t\t\t\tcollage_builder = (lambda x, a=self, PAD=len(pad), WIDTH=range(w // 64): b''.join([b''.join([a.bmps[a.STRIPE_WIDTH*(x+1)-1-k][64*PAD*j:64*PAD*(j+1)] for k in WIDTH]) for j in range(64)]))\n\t\t\telse:\n\t\t\t\tcollage_builder = (lambda x, a=self, PAD=len(pad), WIDTH=range(w // 64): b''.join([b''.join([a.bmps[a.STRIPE_WIDTH*x+k][64*PAD*j:64*PAD*(j+1)] for k in WIDTH]) for j in range(64)]))\n\t\t\tc_bmp += b''.join(map(collage_builder, range(h//64)))\n\t\t\tself.b_write(os.path.join(dname, \"%s_collage.bmp\" % (self.fname)), self.b_export_bmp(w, h, c_bmp))\n\t\t\tself.b_log(sys.stdout, False, 0, \"Successfully exported collage file.\")\n\t\treturn True\n\tdef b_export_bmp(self, width, height, data):\n\t\tif not self.pal:\n\t\t\treturn b\"BM\"+pack(\"<L\", len(data)+122)+b\"\\x00\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"+pack(\"<L\", width)+pack(\"<L\", height)+b\"\\x01\\x00\\x20\\x00\\x03\\x00\\x00\\x00\"+pack(\"<L\", len(data))+b\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFF\\x00\\x00\\xFF\\x00\\x00\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\xFF niW\"+(b\"\\x00\"*36)+b\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"+data\n\t\telse:\n\t\t\treturn b\"BM\"+pack(\"<L\", len(data)+0x36)+b\"\\x00\\x00\\x00\\x00\\x36\\x04\\x00\\x00\\x28\\x00\\x00\\x00\"+pack(\"<L\", width)+pack(\"<L\", height)+b\"\\x01\\x00\\x08\\x00\\x00\\x00\\x00\\x00\"+pack(\"<L\", len(data)-0x400)+b\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"+data\n\tdef b_write(self, fname, data):\n\t\twith open(fname, \"wb\") as f:\n\t\t\tf.write(data)\n\t\treturn True\n\tdef b_flush(self):\n\t\tself.bdat = \"\"\n\t\tself.bmps = []\n\t\tself.o_bmps = []\n\t\treturn True\n\nif __name__ == \"__main__\":\n\tprs = argparse.ArgumentParser(description=\"RDP Bitmap Cache parser (v. 3.04, 2023/12/02)\")\n\tprs.add_argument(\"-s\", \"--src\", help=\"Specify the BMCache file or directory to process.\", required=True)\n\tprs.add_argument(\"-d\", \"--dest\", help=\"Specify the directory where to store the extracted bitmaps.\", required=True)\n\tprs.add_argument(\"-c\", \"--count\", help=\"Only extract the given number of bitmaps.\", type=int, default=-1)\n\tprs.add_argument(\"-v\", \"--verbose\", help=\"Determine the amount of information displayed.\", action=\"store_true\", default=False)\n\tprs.add_argument(\"-o\", \"--old\", help=\"Extract the old bitmap data found in the BMCache file.\", action=\"store_true\", default=False)\n\tprs.add_argument(\"-b\", \"--bitmap\", help=\"Provide a big bitmap aggregating all the tiles.\", action=\"store_true\", default=False)\n\tprs.add_argument(\"-w\", \"--width\", help=\"Specify the number of tiles per line of the aggregated bitmap (default=64).\", type=int, default=64)\n\tprs.add_argument('-k', \"--kape\", help=\"Use this option to split out the different inputs into separate folders\", action=\"store_true\", default=\"False\")\n\targs = prs.parse_args(sys.argv[1:])\n\n\tbmcc = BMCContainer(verbose=args.verbose, count=args.count, old=args.old, big=args.bitmap, width=args.width)\n\tsrc_files = []\n\tif not os.path.isdir(args.dest):\n\t\tsys.stderr.write(\"Destination folder '%s' does not exist.%s\" % (args.dest, os.linesep))\n\t\texit(-1)\n\telif os.path.isdir(args.src):\n\t\tsys.stdout.write(\"[+++] Processing a directory...%s\" % (os.linesep))\n\t\tfor root, dirs, files in os.walk(args.src):\n\t\t\tfor f in files:\n\t\t\t\tif f.rsplit(\".\", 1)[-1].upper() in [\"BIN\", \"BMC\"]:\n\t\t\t\t\tif args.verbose:\n\t\t\t\t\t\tsys.stdout.write(\"[---] File '%s' has been found.%s\" % (os.path.join(root, f), os.linesep))\n\t\t\t\t\tsrc_files.append(os.path.join(root, f))\n\t\tif len(src_files) == 0:\n\t\t\tsys.stderr.write(\"No suitable files were found under '%s' directory.%s\" % (args.src, os.linesep))\n\t\t\texit(-1)\n\telif not os.path.isfile(args.src):\n\t\tsys.stderr.write(\"Invalid -s/--src parameter; use -h/--help for help.%s\" % (os.linesep))\n\t\texit(-1)\n\telse:\n\t\tsys.stdout.write(\"[+++] Processing a single file: '%s'.%s\" % (args.src, os.linesep))\n\t\tsrc_files.append(args.src)\n\tfor src in src_files:\n\t\tsys.stdout.write(\"[+++] Processing a file: '%s'.%s\" % (src, os.linesep)) \t\t\n\t\tif bmcc.b_import(src):\n\t\t\tdestination = args.dest\n\t\t\tif (args.kape == True):\n\t\t\t\tdestination = src.replace(\"\\\\\",\"_\").replace(\"//\",\"_\").replace(\":\",\"_\").replace(\"_AppData_Local_Microsoft_Terminal Server Client_Cache\",\"\")\n\t\t\t\tdestination = args.dest + \"\\\\\" + destination\n\t\t\t\tif not os.path.exists(destination):\n\t\t\t\t\tos.makedirs(destination)\n\t\t\t\n\t\t\tbmcc.b_process()\n\t\t\tbmcc.b_export(destination)\n\t\t\tbmcc.b_flush()\n\t\t\n\t\t\n\tdel bmcc\n"
  }
]