diff --git a/Contents.m b/Contents.m new file mode 100644 index 0000000..f22501f --- /dev/null +++ b/Contents.m @@ -0,0 +1,19 @@ +% megconnectome +% Version 1.0 www.humanconnectome.org 24-Feb-2014 + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README b/README new file mode 100644 index 0000000..715f1db --- /dev/null +++ b/README @@ -0,0 +1,26 @@ +MEGCONNECTOME is the analysis toolbox for the MEG component to the +Human Connectome Project. Unless otherwise noted, this code is +copyright by the Human Connectome Project and its coworkers. + +See http://www.humanconnectome.org for more details. + +----------------------------------------------------------------------- + +The MATLAB code that is compiled into the megconnectome stand-alone +executable application should consist of functions. These function +m-files go into analysis_functions and in trial_functions. + +The MATLAB code that represents the analysis pipeline scripts that +are executed by the megconnectome application should all be +scripts (not functions) and go into pipeline_scripts. + +The build folder contains the MATLAB and shell-scripting code that +is required for building the megconnectome stand-alone executable +application. + +The bin folder contains a collection of Linux shell scripts to +facilitate pipeline execution. It also contains the megconnectome +application. + +The template folder contains a number of template files for +visualization purposes. diff --git a/analysis_functions/hcp_ICA_RMEG_classification.m b/analysis_functions/hcp_ICA_RMEG_classification.m new file mode 100644 index 0000000..d606ea1 --- /dev/null +++ b/analysis_functions/hcp_ICA_RMEG_classification.m @@ -0,0 +1,361 @@ +function [comp_best] = hcp_ICA_RMEG_classification(ref_data,options,iteration,datain) + +% FUNCTION hcp_ICA_RMEG_classification +% +% hcp_ICA_RMEG_classification performs the classification of the output +% of an Independent Component Analyis arranged in an "iteration" structure. +% This structure must contains "comp" fields coming from an iterative procedure +% using ft_componentanalysis (like hcp_doICARMEG). In general the iteration structure +% contains a number of "comp" fields as many as the number of ICA iteration performed. +% +% Use as +% ICA = hcp_ICA_RMEG_classification(options, iteration, datain) +% +% where datain is data structure used as input in the ft_componentanalysis +% and options is a cell-array specifying the behaviour of the +% algorithm. Cell-arrays need to be organized as sets of key-value pairs, +% i.e. {'key1', 'value1', ...}. +% +% Options needs to contain the following key: +% dataset: dataset containing the reference signals +% ref_channels: reference channel labels (default hcp-labels {'E31', 'E32'}) +% +% Plotting and storing options +% showbestit: summary plots of the selected best iteration (default='yes') +% plottype: plot layout (summary or components, default = 'sumamry' [see hcp_plotICA]) +% saveres: save the summary plot (default = 'no') using the 'saveformat' file extention (defualt = 'fig') +% store_bestcomp: store the comp structure for the best iteration with the time courses and +% the time vector (default = 'yes') +% +% Otions by default retrieved from the comp structure to be modified with care if required in particular case +% bandpass: pass-band interval ([f1 f2]) +% bandstop: stop-band intervals ([fs1 fs2]) +% grad: gradiometer structure +% +% Example use: +% options = {'dataset', '0' , 'ref_channels', {'E31', 'E32'}, 'saveres', 'yes', 'saveformat', 'jpg'}; +% ICA = hcp_ICA_RMEG_classification(options,iteration,megdata); + +% set the defaults +% cfgin.dataset = ft_getopt(options, 'dataset'); +% cfgin.dataset=fname; +subject = ft_getopt(options, 'subject'); + +cfgin.ref_channels = ft_getopt(options, 'ref_channels'); +cfgin.bpfreq = ft_getopt(options, 'bandpass'); +cfgin.bsfreq = ft_getopt(options, 'bandstop'); +cfgin.skipped_intervals = ft_getopt(options, 'skipped_intervals'); +cfgin.grad = ft_getopt(options, 'grad'); + +cfgin.showbestit = ft_getopt(options, 'showbestit'); +cfgin.plottype = ft_getopt(options, 'plottype'); + +cfgin.store_bestcomp = ft_getopt(options, 'store_bestcomp'); +cfgin.saveres = ft_getopt(options, 'saveres'); +cfgin.saveformat = ft_getopt(options, 'saveformat'); + +% if isempty(cfgin.dataset) , cfgin.dataset = '0'; end +if isempty(cfgin.ref_channels) , cfgin.ref_channels = {'E31', 'E32'}; end +if isempty(cfgin.bpfreq) , cfgin.bpfreq = iteration(1,1).comp(1,1).bandpass ; end +if isempty(cfgin.bsfreq) , cfgin.bsfreq = iteration(1,1).comp(1,1).bandstop ; end +% if isempty(cfgin.grad) , sens=ft_read_sens(cfgin.dataset) ; cfgin.grad = sens ; end +if isempty(cfgin.showbestit), cfgin.showbestit = 'no'; end +if isempty(cfgin.plottype) , cfgin.plottype = 'summary'; end +if isempty(cfgin.store_bestcomp) , cfgin.store_bestcomp = 'yes'; end +if isempty(cfgin.saveres) , cfgin.saveres = 'no'; end +if isempty(cfgin.saveformat) , cfgin.saveformat = 'fig'; end +cfgin.zlim='maxabs'; + +nref=0; + +if isfield(cfgin,'ref_channels') + + % if ischar(ref_data) + % options=ft_setopt(options,'channels',cfgin.ref_channels) + % ref_data = hcp_ICA_preprocessing(ref_data, options); + % end + + nref= size(ref_data.trial{1},1); + ntrl= size(ref_data.trial,2) + + % ----- Normalize ECG and EOG data + for i = 1:nref + for k=1:ntrl + sig=ref_data.trial{k}(i,:); + if std(sig) > 0 + sig=(sig-mean(sig))/std(sig); + ref_data.trial{k}(i,:)=sig; + end + end + end + elec=cell2mat(ref_data.trial); + + h=figure; + set(h, 'visible', 'off','paperposition', [1 1 10 7]); + for i = 1:nref + subplot(ceil(nref/2),2,i); plot(cell2mat(ref_data.time),elec(i,:)); axis tight; + ylabel(ref_data.label(i)); + xlabel('seconds'); + title('Reference data timecourses','FontSize',12); + end + imgname=[subject '_icaclass_refch']; + hcp_write_figure(imgname, h); + clear h; % FIXME should this be clear or close? + + % ----- Time Course of Power of Electric Channels ----------------------- + + cfg = []; + + cfg.bpfilter = 'yes'; + cfg.bpfreq = cfgin.bpfreq; + cfg.hilbert='abs'; + + pow_data = ft_preprocessing(cfg,ref_data); + + pelec = cell2mat(pow_data.trial).^2; + + % ----- Power Spectral Estimation of Electric Channels ------------------ + + win=2048/(ref_data.fsample*2^(round(log2(1024/ref_data.fsample)))); + + cfg = []; + cfg.length = win; + ref_data_seg = ft_redefinetrial(cfg, ref_data); + + cfg=[]; + cfg.method = 'mtmfft'; + cfg.output = 'pow'; + cfg.trials = 'all'; + cfg.taper = 'hamming'; + + [freq_ref_data] = ft_freqanalysis(cfg,ref_data_seg); + + selec = sqrt(freq_ref_data.powspctrm); +end + +%% Performing IC CLASSIFICATION + +n_iter=size(iteration,2); +for j=1:n_iter + clear pow_data A W IC pIC pow_f_IC freq_comp_tr good_ic mspettro G G2 G3 H H2 H3 K K2 K3R R2 R3 ave T T1 T2 ave_on ave_off clas potenza order spec_on spec_off + + comp_iter = iteration(1,j).comp; + comp_iter.time=datain.time; + for it=1:ntrl + comp_iter.trial{it}=comp_iter.unmixing*datain.trial{it}; + end + Nc = size(comp_iter.topo,2); + + + + %------ POWER SPECTRAL DENSITY ESTIMATION ----- + cfgold=comp_iter.cfg; + comp_iter= hcp_ICA_freq(comp_iter, options, datain); + comp_iter.cfgold=cfgold; + freq_comp =comp_iter.freq_comp; + temp_struc(j).freq_comp=freq_comp; + + mspettro = sqrt(freq_comp.powspctrm); + F = freq_comp.freq; + + %-------------------------------- + %----> IC POWER TIME COURSE <---- + %-------------------------------- + + % cfg = []; + % + % cfg.bpfilter = 'yes'; %'no' or 'yes' bandpass filter (default = 'no') + % cfg.bpfreq = cfgin.bpfreq; + % cfg.hilbert='abs'; + % + % pow_data = ft_preprocessing(cfg,comp_iter); + % + pow_data=comp_iter.pow_data; + temp_struc(j).pow_data=pow_data; + pIC = cell2mat(pow_data.trial).^2; + + %------------------------------------- + %--------> IC CLASSIFICATION <-------- + %------------------------------------- + + if Nc==0 + good_ic=[]; art_con=1; G=[]; G2=[]; G3=[]; + H=[]; H2=[]; H3=[]; + else + + % -----Correlation between ICs and electric channels <--- + + IC=cell2mat(comp_iter.trial); + if isfield(cfgin,'ref_channels') + % elec=ref_data.trial{1}; + if nref > 0 + + % ----- Correlation between the ICs and electric channels + R = corrcoef([elec',IC']); + K = R(nref+1:nref+size(comp_iter.topo,2),1:nref); + H = max(abs(K'),[],1); + + % ----- Correlation between the ICs' and electric channels' power in window of 400 ms duration (step = 20 ms) + R2 = corrcoef([pelec',pIC']); + K2 = R2(nref+1:nref+size(comp_iter.topo,2),1:nref); + H2 = max(abs(K2'),[],1); + + % ----- Correlation between ICs' electric channels'power spectra between 3 and 40 Hz + + vspec = find(F > 3 & F < 40); + R3 = corrcoef([selec(:,vspec)',mspettro(:,vspec)']); + K3 = R3(nref+1:nref+size(comp_iter.topo,2),1:nref); + H3 = max(abs(K3'),[],1); + else + H = zeros(1,size(comp_iter.topo,2)); H2=zeros(1,size(comp_iter.topo,2)); H3=zeros(1,size(comp_iter.topo,2)); + end + else + H=zeros(1,Nc); + H2=zeros(1,Nc); + H3=zeros(1,Nc); + end + + + % ----- Fit with 1/f spectrum, flat spectrum "quantification", IC time kurtosis + + + %!FROM GIORGOS: + % Replaced below the fittype function with hcp_fitspectrum, so the compiled + % version can run on WashU, according to instructions from the help + % of the function i.e.: + % ------------------------------------------ + % Example: + % x = (1:100)'; + % y = 2./x + 3 + 0.1*rand(size(x)); + % [fitx, goodness] = hcp_fitspectrum(x, y) + % + % This replaces the following use of the fit() function from + % the Mathworks curve fitting toolbox + % g = fittype('abs(a)*1/x + abs(b)') + % [fitx, goodness] = fit(x, y, g) + %-------------------------------------------- + %g = fittype('abs(a)*1/x + b'); % COMMENTED OUT BY GIORGOS + frq = [cfgin.bpfreq(1,1) 13]; + frq2 = [2 78]; + vec = find(F > frq(1) & F < frq(2)); + vec2 = find(F > frq2(1) & F < frq2(2)); + for ix = 1:Nc + sspettro = mspettro(ix,:); + sspettro_fit=sspettro./sspettro(vec(1,1)); + %[fitx,goodness] = fit(F(vec)',sspettro_fit(vec)',g); % COMMENTED OUT BY GIORGOS + [fitx,goodness] = hcp_fitspectrum(F(vec)',sspettro_fit(vec)'); + if(fitx.a>0) + G(ix) = goodness.rsquare; + else + G(ix)=0.11; + end + ss = sspettro(vec2); + G2(ix)= prctile(ss,95)/(median(ss)); + G3(ix)= kurtosis(IC(ix,:)); + end + + % ---- Set thresholds for ICs' classification + thres_a = 0.10; % Elc-IC Signal Correlation + thres_b = 0.25; % Elc-IC Power Correlation + thres_c = 0.95; % Elc-IC Spectrum Correlation + thres_d = 0.5; % PSD 1/f + thres_e = 2.02; % Spectrum Flat + thres_f = 15; % IC Time Kurtosis + % thres_g = 0.1; + % thres_h = 15; + + %---------------------------------------------------------- + % classification: 0=artifact 1=brain signal + %---------------------------------------------------------- + + clas = zeros(1,Nc); + + clas_ecg_eog = zeros(1,Nc); + + %------ Find correlation with ECG and EOG + clas_ecg_eog(find(H > thres_a | H2 > thres_b | H3 > thres_c)) = 1; %---> Elc_signal_correlation - Elc_power_correlation - Elc_spectrum_correlation + %------ + ecg_eog_ic = find(clas_ecg_eog==1); + + + clas(find(G2 > thres_e & G3 < thres_f & H3 < thres_c)) = 1; %---> Spectrum_flat - Time_kurtosis - Elc_spectrum_correlation + + clas(find(G > thres_d | H > thres_a | H2 > thres_b)) = 0; %---> Spectrum_one_over_f - Elc_signal_correlation - Elc_power_correlation + + good_ic = find(clas==1); + + if nref > 0 + art_con = mean(H2(good_ic)); + else + art_con = 0; + end + + end + + + + % -------- Save Classification Parameters -------------- + + + iteration(j).comp.class.total_ic_number=size(comp_iter.topo,2); + iteration(j).comp.class.brain_ic_number=length(good_ic); + iteration(j).comp.class.brain_ic=good_ic; + iteration(j).comp.class.ecg_eog_ic=ecg_eog_ic; + iteration(j).comp.class.artifact_contamination=art_con; + iteration(j).comp.class.elc_signal_correlation=H; + iteration(j).comp.class.elc_power_correlation=H2; + iteration(j).comp.class.elc_spectrum_correlation=H3; + iteration(j).comp.class.spectrum_one_over_f=G; + iteration(j).comp.class.spectrum_flat=G2; + iteration(j).comp.class.time_kurtosis=G3; + + goodness_ic(j,1) = length(good_ic); + goodness_ic(j,2) = 1-art_con; + + ica_nonlinearity=comp_iter.cfgold.fastica.g; + disp([' run ' num2str(j) ' \ ' num2str(n_iter)]); + disp(['total number of ICs : ' num2str(size(comp_iter.topo,2)) ' - number of brain ICs : ' num2str(length(good_ic)) ' - artifact contamination : ' num2str(art_con)]); + +end + +ICA.iteration = iteration; + +clear comp_iter A W IC pIC good_ic mspettro G G2 G3 H H2 H3 K K2 K3 R R2 R3 ave ave_on ave_off T T1 T2 clas spec_on spec_off; + + +% ------- Save Best Iteration Data -------------- + +[tmp,pos_new]=max(goodness_ic(:,1).*goodness_ic(:,2)); + +W = ICA.iteration(pos_new).comp.unmixing; +good_ic = ICA.iteration(pos_new).comp.class.brain_ic; +comp=ICA.iteration(pos_new).comp; + +comp_best=ICA.iteration(pos_new).comp; + +comp.time=datain.time; +for iic=1:size(datain.trial,2) + comp.trial{iic}=W*datain.trial{iic}; +end +comp.freq_comp=temp_struc(pos_new).freq_comp; +comp.pow_data=temp_struc(pos_new).pow_data; +comp.grad=cfgin.grad; + +% ------- Display Best Iteration -------------- +if(strcmp(cfgin.showbestit,'yes')) + hcp_ICA_plot(comp,options); +end + +if ~isempty(W) + ICA.best_iter.index = pos_new; + ICA.best_iter.nonlinearity = ica_nonlinearity; + ICA.best_iter.brain_ic_number = length(good_ic); + ICA.best_iter.brain_ic = good_ic; + if strcmp(cfgin.store_bestcomp,'yes') + ICA.best_iter.comp=comp; + end +else + disp('!!!ATTENTION:ICA processing completed, but no brain IC!'); +end + +end diff --git a/analysis_functions/hcp_ICA_freq.m b/analysis_functions/hcp_ICA_freq.m new file mode 100644 index 0000000..9005f40 --- /dev/null +++ b/analysis_functions/hcp_ICA_freq.m @@ -0,0 +1,98 @@ +function [comp] = hcp_ICA_freq(comp, options, datain) + +% HCP_ICA_FREQ allows basic frequency analysis and plots Independent +% Components computed using the FT_COMPONENTANALYSIS function. This +% function estimate the IC Power Spectrum Density and plots the topographic +% distribution (using FT_TOPOPLOTIC), the Power Spectrum Density and the +% Time Course of the Indipendent Components, with two different layouts +% (see cfg.plottype) +% +% Use as +% hcp_ICA_freq(comp, options, datain) +% +% where the input comp structure should be obtained from +% FT_COMPONENTANALYSIS and the datain structure is a fieldtrip data +% structure contining the channel time courses used as imput of the +% FT_COMPONENTANALYSIS . +% +% Options needs to contain the following keys: +% doplot: 'no' or 'yes' to plot the results of the frequency analysis +% (default='no') +% +% Options used by hcp_plotICARMEG +% component: field that contains the list of independent component(s) to +% be plotted as color (default all) +% frange: frequency range of the PSD plot ( default is [1 150] or +% comp.fsample/2 if comp.fsample<375 Hz) +% plottype: 'summary' or 'components' (default = 'summary') +% saveres: 'yes' or 'no' save the resutls in a file (default = 'no') +% saveformat: image file format (default 'fig') +% grad: fieldtrip gradiometer structure + +switch nargin + case 3 + % reconstruct the component time courses from the data and the unmixing + % matrix + % comp.time = datain.time; + cfg = []; + cfg.unmixing = comp.unmixing; + cfg.topolabel = comp.topolabel; + cfg.order = comp.order; + comp = ft_componentanalysis(cfg, datain); + comp.order = cfg.order; + % comp.trial{1} = comp.unmixing*datain.trial{1}; + case 2 + % check whether IC time courses are in the first input + if ~isfield(comp,'trial') , error('IC time course not defined, data matrix required'); end + case 1 + error('too few input arguments'); + % FIXME in principle this should work because then all options should + % be set to default within the function + if ~isfield(comp,'trial') , error('IC time course not defined, data matrix required'); end + otherwise + error('too many input arguments'); +end + +cfgin.doplot = ft_getopt(options, 'doplot', 'no'); % FIXME I think the plotting should not be done within this function +cfgin.grad = ft_getopt(options, 'grad'); + +if ~isempty(cfgin.grad), + comp.grad = cfgin.grad; +end + +%----> POWER SPECTRAL DENSITY ESTIMATION <----- +win = 2048/(comp.fsample*2^(round(log2(1024/comp.fsample)))); + +cfg = []; +cfg.length = win; +comp_seg = ft_redefinetrial(cfg, comp); + +cfg = []; +cfg.method = 'mtmfft'; +cfg.output = 'pow'; +cfg.trials = 'all'; +cfg.taper = 'hamming'; + +freq_comp = ft_freqanalysis(cfg,comp_seg); +comp.freq_comp=freq_comp; + +%-------------------------------- +%----> IC POWER TIME COURSE <---- +%-------------------------------- + +cfg = []; +cfg.bpfilter = 'no'; %'no' or 'yes' bandpass filter (default = 'no') +cfg.bpfreq = [1 150]; +cfg.hilbert = 'abs'; +pow_data = ft_preprocessing(cfg,comp); + +for k = 1:numel(pow_data.trial) + pIC{k} = pow_data.trial{k}.^2; +end + +comp.pIC = pIC; +comp.pow_data = pow_data; + +if strcmp(cfgin.doplot,'yes') + hcp_ICA_plot(comp,options); +end diff --git a/analysis_functions/hcp_ICA_plot.m b/analysis_functions/hcp_ICA_plot.m new file mode 100644 index 0000000..1b924ae --- /dev/null +++ b/analysis_functions/hcp_ICA_plot.m @@ -0,0 +1,191 @@ +function hcp_ICA_plot(comp_freq, options) + +% hcp_ICA_plot allows summury plots of Independent Components computed using +% the hcp_ICA_freq function. +% This function plots the topographic distribution (using FT_TOPOPLOTIC), +% the Power Spectrum Density and the Time Course of the Indipendent Components, with +% two different layouts (see cfg.plottype) +% +% Use as +% hcp_ICA_freq(comp_freq, options, datain) +% where the input comp_freq structure should be obtained from FT_COMPONENTANALYSIS and +% the datain structure is a fieldtrip data structure contining the channel time courses +% used as imput of the FT_COMPONENTANALYSIS . +% +% Options needs to contain the following key: +% component : field that contains the independent +% component(s) to be plotted as color (default all) +% plottype : 'summary' or 'components' (default = 'summary') +% frange : frequency range of the PSD plot (default is [1 150] or comp_freq.fsample/2 if comp_freq.fsample<375 Hz); +% saveres : 'yes' or 'no' save the resutls in a file (default = 'no') +% saveformat : image file format (default 'fig') +% grad : fieldtrip gradiometer structure + +Nc = size(comp_freq.topo,2); + +cfgin.plottype = ft_getopt(options, 'plottype', 'components'); %summary or components +cfgin.saveres = ft_getopt(options, 'saveres'); +cfgin.saveformat = ft_getopt(options, 'saveformat', 'fig'); +cfgin.fileout = ft_getopt(options, 'fileout'); +cfgin.parameter = ft_getopt(options, 'parameter', 'topo'); +cfgin.component = ft_getopt(options, 'component', 1:Nc); +cfgin.comment = ft_getopt(options, 'comment', 'no'); +cfgin.frange = ft_getopt(options, 'frange'); +cfgin.grad = ft_getopt(options, 'grad'); +cfgin.dataset = ft_getopt(options, 'dataset'); +cfgin.zlim = ft_getopt(options, 'zlim', 'maxabs'); +cfgin.colorbar = ft_getopt(options, 'colorbar', 'yes'); +cfgin.posi = ft_getopt(options, 'position'); +fvis = ft_getopt(options, 'visible', 'on'); +cfgin.modality = ft_getopt(options, 'modality','MEG'); + +if strcmp(cfgin.modality,'MEG') + cfgin.layout='4D248.mat'; +elseif strcmp(cfgin.modality,'EEG') + cfgin.layout='EEG1010.lay'; +end + +if isempty(cfgin.grad) + if ~isempty(cfgin.dataset) + sens = ft_read_sens(cfgin.dataset) ; + cfgin.grad = sens; + comp_freq.grad = cfgin.grad; + else + cfgin.grad = comp_freq.grad; + warning('myControl:FieldTrip','the gradiometer structure may be undefined (correctly)') + end +end + +% Set the High Frequency limit in the PSD plot +if isempty(cfgin.frange) + if(comp_freq.fsample>375) + frange = [1 150]; % default passband if comp_freq.fsample>375 Hz + else + frange = [1 (floor(comp_freq.fsample/20)*10)]; % set highest frequency to the Nyquist freq if comp_freq.fsample<375 Hz + end +else frange = cfgin.frange; +end + +% create temporary variable +selcomp = cfgin.component; + +freq_comp = comp_freq.freq_comp; +mspec = sqrt(freq_comp.powspctrm(selcomp,:)); +F = freq_comp.freq; + +if strcmp(cfgin.plottype,'summary') + + %----> Topological maps of mixing matrix columns <---- + f1 = figure; + set(f1, 'Position', [50 50 1700 900]); + ft_topoplotIC(cfgin, comp_freq) + + %----> POWER SPECTRAL DENSITY PLOTTING <----- + + % allow multiplotting + nplots = numel(selcomp); + nyplot = ceil(sqrt(nplots)); + nxplot = ceil(nplots./nyplot); + + f2 = figure; + set(f2, 'Position', [50 50 1700 900]); + for i = 1:length(selcomp) + subplot(nxplot, nyplot, i); + plot(F,mspec(i,:)); + title(['PSD ic ' num2str(selcomp(i))]); + Prms = mspec(i,:); + lags = find(F>1 & F IC TIME COURSE <---- + + f3 = figure; + set(f3, 'Position', [50 50 1700 900]); + for i = 1:length(selcomp) + subplot(nxplot, nyplot, i); hold on + for m = 1:numel(comp_freq.trial) + plot(comp_freq.time{m}, comp_freq.trial{m}(selcomp(i),:)); + end + title(['Timecourse ic ' num2str(selcomp(i))]); + end + + if (strcmp(cfgin.saveres,'yes')) + hcp_write_figure('topo_IC_summary', f1, 'format', cfgin.saveformat); + hcp_write_figure('PSD_IC_summary', f2, 'format', cfgin.saveformat); + hcp_write_figure('TC_IC_summary', f3, 'format', cfgin.saveformat); + end + +elseif strcmp(cfgin.plottype,'components') + + leg = char('OneOverF (>0.5) ' , 'Specflat (<2.0) ' , 'Kurtosis (>15) ' , 'elecSig (>0.1) ', 'elecPow (>0.25) ', 'elecSpe (>0.95) ', 'Brain '); + + for ix = 1:length(selcomp) + if(~isempty(cfgin.posi)) + f = figure; + set(f, 'visible', fvis,'on','Position', cfgin.posi); + % set(f, 'Position', cfgin.posi) + else + f = figure; + set(f, 'visible', fvis); + end + + subplot(2,2,1) + cfgin.component = selcomp(ix); + ft_topoplotIC(cfgin, comp_freq); + xlabel(['IC ' num2str(selcomp(ix))],'color','k'); + title('IC sensor map','FontSize',12); + + subplot(2,2,[3 4]) + plot(F,mspec(ix,:)); + title(['IC ' num2str(selcomp(ix))]); + Prms = mspec(ix,:); + lags = find(F>1 & F1) + hcp_write_figure([cfgin.fileout '_' num2str(selcomp(ix))], f, 'format', cfgin.saveformat) + else + hcp_write_figure(cfgin.fileout, f, 'format', cfgin.saveformat) + end + close + clear f; + end + end + +else error('plot settings must be summary or components'); +end diff --git a/analysis_functions/hcp_ICA_plotclassification.m b/analysis_functions/hcp_ICA_plotclassification.m new file mode 100644 index 0000000..047448e --- /dev/null +++ b/analysis_functions/hcp_ICA_plotclassification.m @@ -0,0 +1,180 @@ +function hcp_ICA_plotclassification(comp,options_ICA,datain) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +subject = ft_getopt(options_ICA, 'subject'); +bandpass = ft_getopt(options_ICA, 'bandpass'); +bandstop = ft_getopt(options_ICA, 'bandstop'); +grad = ft_getopt(options_ICA, 'grad'); +modality = ft_getopt(options_ICA, 'modality', 'MEG'); % GIORGOS + +if strcmp(modality,'MEG') + layout='4D248.mat'; +elseif strcmp(modality,'EEG') + layout='EEG1010.lay'; +end + +for i=1:size(datain.trial,2) + comp.trial{i} = comp.unmixing*datain.trial{i}; +end + +comp.time={cell2mat(datain.time)}; + + +if(~isfield(comp,'pow_data')) + doplot='no'; % Summury Results cn be plotted also at this level without calling hcp_plotICA + options = {'doplot',doplot, 'grad', grad,'plottype','components'}; + + [comp_freq]=hcp_ICA_freq(comp, options, datain); + comp.pow_data=comp_freq.pow_data; + comp.freq_comp=comp_freq.freq_comp; +end +n_IC=size(comp.unmixing,1); + +frange=bandpass; %to be decided if in compo or other + +cfgin =[]; +cfgin.grad=grad; +cfgin.zlim='maxabs'; +cfgin.component=[]; +cfgin.parameter = 'topo'; +cfgin.comment = 'no'; +cfgin.colorbar = 'yes'; + + +mspec = sqrt(comp.freq_comp.powspctrm); +F = comp.freq_comp.freq; +pow_data=comp.pow_data; + +pIC = cell2mat(pow_data.trial).^2; +time_pIC=cell2mat(pow_data.time); + + +leg={'OneOverF (>0.5) ' ; 'Specflat (<2.0) ' ; 'Kurtosis (>15) ' ; 'elecSig (>0.1) '; 'elecPow (>0.25) '; 'elecSpe (>0.95) '}; +thrs=[0.5;2.0;15;0.1;0.25;0.95]; +class=comp.class; +% size_s=get(0,'ScreenSize'); +for ix=1:n_IC + str(1,:)={num2str(abs(comp.class.spectrum_one_over_f(1,ix)),'%5.3f')}; + str(2,:)={num2str(abs(comp.class.spectrum_flat(1,ix)),'%5.1f')}; + str(3,:)={num2str(abs(comp.class.time_kurtosis(1,ix)),'%5.1f')}; + str(4,:)={num2str(abs(comp.class.elc_signal_correlation(1,ix)),'%5.3f')}; + str(5,:)={num2str(abs(comp.class.elc_power_correlation(1,ix)),'%5.3f')}; + str(6,:)={num2str(abs(comp.class.elc_spectrum_correlation(1,ix)),'%5.3f')}; + strcolor=['k' ;'k'; 'k'; 'k'; 'k'; 'k']; + for i=[1 3 4 5 6] + if(str2num(str{i})>thrs(i)) strcolor(i)='b';end + end + if(str2num(str{2})1 & F. + +%%%%%%%%% deal with the input options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% get the options +selchan = ft_getopt(options, 'channels', 'MEG'); +knownbadchan = ft_getopt(options, 'knownbadchannels', []); % GIORGOS +resamplefs = ft_getopt(options, 'resamplefs'); +dec = ft_getopt(options, 'decimation'); +bpfreq = ft_getopt(options, 'bandpass'); +bsfreq = ft_getopt(options, 'bandstop'); +bad_segments = ft_getopt(options, 'skipped_intervals'); + +%-------------------------- +% GIORGOS +if ~isempty(knownbadchan) + + selchan=[selchan, knownbadchan]; + +end +%-------------------------- + +% check the options +doresample = false; +if ~isempty(resamplefs) && ~isempty(dec) + error('ambiguous definition using both resamplefs and decimation'); +elseif ~isempty(resamplefs) + fsample = resamplefs; + doresample = true; +elseif ~isempty(dec) + fsample = dataraw.fsample/dec; + doresample = true; +else + fsample = dataraw.fsample; % don't do resampling +end + +if isempty(bpfreq) + if(fsample>375) + bpfreq = [1 150]; % default passband in Hz + else + % ensure the high frequency cut of is sufficiently below the Nyquist freq + bpfreq = [1 floor(fsample/25)*10]; + end +end + +if fsample<2*bpfreq(1,2), + error('Sampling frequency must be > 2*bpfreq(1,2)'); +end + +%%%%%%%%% MEG channels preprocessing %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Reading from file and filtering +% cfg = []; +% cfg.channel = selchan; +% cfg.bpfilter = 'yes'; +% cfg.bpfreq = bpfreq; +% if ~isempty(bsfreq) +% cfg.bsfilter = 'yes'; +% cfg.bsfreq = bsfreq; +% end +% data = ft_preprocessing(cfg,dataraw); + +cfgin = []; +cfgin.channel = selchan; +cfgin.hpfilter = 'yes'; +cfgin.hpfreq = bpfreq(1,1); +if(bpfreq(1,1)<2) + cfgin.hpfiltord=4; +end +junk = ft_preprocessing(cfgin,dataraw); + +cfgin = []; +cfgin.lpfilter = 'yes'; +cfgin.lpfreq = bpfreq(1,2); +junk2 = ft_preprocessing(cfgin,junk); + +if ~isempty(bsfreq) + cfgin = []; + cfgin.bsfilter = 'yes'; + cfgin.bsfreq = bsfreq; + data = ft_preprocessing(cfgin,junk2); +else + data=junk2 +end + +% Remove the bad segments +% if strcmp(dopreproc,'n') +ntmp=size(bad_segments,1); +datalength=size(dataraw.trial{1},2); +cutwin_filt=round(fsample*3); +bad_segments(ntmp+1,:)=[1 cutwin_filt]; +bad_segments(ntmp+2,:)=[datalength-cutwin_filt datalength]; + +% cutwin_filt=round(fsample*50); +% cutwin_filt2=round(fsample*25); +% bad_segments(ntmp+1,:)=[1 cutwin_filt]; +% bad_segments(ntmp+2,:)=[datalength-cutwin_filt2 datalength]; + +cfg = []; +cfg.artfctdef.badsegments.artifact = bad_segments; +cfg.artfctdef.reject = 'partial'; +data = ft_rejectartifact(cfg, data); +% end + +% Resampling +if doresample + cfg = []; + cfg.resamplefs = fsample; + cfg.detrend = 'no' ; + cfg.demean = 'no' ; + cfg.feedback = 'text'; + cfg.trials = 'all' ; + data = ft_resampledata(cfg, data); +end diff --git a/analysis_functions/hcp_ICA_qualitycheck_pipeline.m b/analysis_functions/hcp_ICA_qualitycheck_pipeline.m new file mode 100644 index 0000000..9fd4a02 --- /dev/null +++ b/analysis_functions/hcp_ICA_qualitycheck_pipeline.m @@ -0,0 +1,551 @@ +function [ bad_segments, bad_channels , iter_res] = hcp_ICA_qualitycheck_pipeline(dataraw,options_ICA) + +% HCP_ICA_QUALITYCHECK_PIPELINE allows the rejection of channels and intervals +% harmful for an Independent Component Analysis. It is babsed on an +% iterative procedure updating the list of bad segments and channels each +% iteration ending when no channels and intervals are found. +% +% Output data are vectors containing the bad channels and intervals +% selected. +% +% Use as +% [skint, bch, iter_res] = hcp_ICA_qualitycheck_pipeline(filename, options_ICA) +% +% where filename is a string that points to a raw data file in the database +% and options_ICA is a cell-array specifying the behaviour of the +% algorithm. Cell-arrays need to be organized as sets of key-value pairs, +% i.e. {'key1', 'value1', ...}. +% +% Options needs to contain the following key: +% channels: channel selection , it should be a row cell +% knownbadchannels: bad channels already known to be bad, it should be a row cell -GIORGOS +% skipped_intervals: Nx2 matrix of skipped intervals previusoly selected (t11 t12 ; t21 t22; ... ; tn1 tn2) +% bandpass (optional): band pass intervals ([f1 f2] default [1 150]) +% bandstop (optional): band stop intervals ([fs1 fs2] default [59 61 ; 119 121]) +% +% The following steps are performed: +% -reading in all data from disk +% -band pass and if required band stop filtering of the selected channels +% -FastICA analysis +% -Find Bad Channels according to the weight of the Mixing matrix +% -Find large artifact in the IC time courses according to a local variance +% -Selects the intervals to be cutted +% +% Example use: +% fname='0'; +% options_ICA = {'channels', 'MEG', 'skipped_intervals', [], 'bandpass', [1 150], 'bandstop', [59 61 ; 119 121]}; +% [skipped_intervals, bad_channels ] = hcp_ICA_qualitycheck_pipeline(fname,options_ICA) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +resultprefix = ft_getopt(options_ICA, 'resultprefix'); +skintpt = ft_getopt(options_ICA, 'skipped_intervals'); % skipped intervals previously identified +skint= skintpt/dataraw.fsample; +plotics = ft_getopt(options_ICA, 'plotics', 'no'); % 'yes' or 'no' to plot the ICA result each iteration (default='no') +ch_method = ft_getopt(options_ICA, 'bad_ch_method', 'std_thr'); % 'std_thr' or 'max_weight' (default='std_thr') +selmode = ft_getopt(options_ICA, 'mode', 'auto'); % skipped interval selection 'auto' or 'user' (default='auto') +saveit = ft_getopt(options_ICA, 'iter_results'); % save the results of each iteration +modality = ft_getopt(options_ICA, 'modality','MEG'); %Francesco +resamplefs = ft_getopt(options_ICA, 'resamplefs'); + +% Add some options to the options_ICA cell-array that will be passed to +% lower level functions. Should these be configurable? +options_ICA = ft_setopt(options_ICA, 'save_comp', 'yes'); +options_ICA = ft_setopt(options_ICA, 'ica_iterations', 1); + +decfactor=1; +if(~isempty(resamplefs)) + decfactor=dataraw.fsample/resamplefs; % decimation factor (double) +end + +%------------------------ +% GIORGOS +if strcmp(modality,'MEG') + layout='4D248.mat'; + threshold_std=10; +elseif strcmp(modality,'EEG') + layout='EEG1010.lay'; + threshold_std=15; % The original threshold for MEG was 10 but many channels were identified as bad. Increased the threshold to be adapted to the EEG signal characteristics +end +%----------------------------- + +% These are hard coded thresholds. Should we consider to make them +% configurable? +num_sig = 12; % number of standard deviation out of the mean from the artifact detection +sic_thr_par = 5; % factor of multiplication of the threshold for the determination of the intervals to be skipped + + +% size_s = get(0,'ScreenSize'); +size_s = [1,1 1200 700]; +flag = 1; % flag that checks convergence +it = 0; % iteration counter +bch2 = []; % some variable +segcount = 0; % some variable +while flag + clear datain + disp('doing ICA preprocessing') + datain = hcp_ICA_preprocessing(dataraw, options_ICA); + disp('STARTING hcp_ICA_unmix'); + iteration = hcp_ICA_unmix(datain, options_ICA); + disp('DONE hcp_ICA_unmix'); + comp = iteration.comp; + + % normalize the time courses to unit standard deviation (and zero mean) + % -> this shouldn't matter because the component time courses are by + % definition zero mean + disp('STARTING ft_channelnormalise'); + tmp = ft_channelnormalise([], comp); + disp('DONE ft_channelnormalise'); + comp.trial = tmp.trial; + + % perform spectral analysis on ICs + options = {'doplot', 'no', 'grad', dataraw.grad, 'plottype', 'summary'}; % perform frequency analysis + disp('STARTING ft_ICA_freq'); + comp_freq = hcp_ICA_freq(comp, options); + disp('DONE ft_ICA_freq'); + + disp(['plotics:',plotics]) + if strcmp(plotics, 'yes') + options = {'plottype', 'components'}; + disp('STARTING ft_ICA_plot'); + hcp_ICA_plot(comp_freq, options) % summary plots of the IC + disp('DONE ft_ICA_plot'); + end + + % HACK JM work it back into the 1 trial case in order for the rest of the + % code to work: I suggest however to clean this up at some point + disp('STAGE POST A'); + fs = 0; + nsmp = 0; + for k = 1:numel(comp.trial) + fs = fs + sum(diff(comp.time{k})); + nsmp = nsmp + numel(comp.time{k}) - 1; + end + fs = nsmp./fs; + + % timelim = [comp.time{1}(1) comp.time{end}(end)]; + % timeaxis = timelim(1):1/fs:timelim(2); + if(isempty(resamplefs)) + nSamples = dataraw.sampleinfo(1,2); + else + nSamples = round(dataraw.sampleinfo(1,2)*(datain.fsample/dataraw.fsample)); + end + disp('STAGE POST B'); + timelim = [1 nSamples]; + timeaxis = [0:timelim(2)]/fs; + + Nc = size(comp.topo,2); + ntim = numel(timeaxis); + + % Reconstruct the whole IC signal and power adding zeros in the skipped intervals + disp('STAGE POST C'); + IC = zeros(Nc, nSamples); + pIC = zeros(Nc, nSamples); + for i=1:numel(comp.trial) + begindx = nearest(timeaxis, comp.time{i}(1)); + endindx = nearest(timeaxis, comp.time{i}(end)); + IC(:,begindx:endindx) = comp_freq.trial{i}; + pIC(:,begindx:endindx) = comp_freq.pIC{i}; + end + disp('STAGE POST D'); + % Smoothing of the IC Power Time Course (used in the peak analysis) + sIC = zeros(size(pIC)); + for i=1:size(IC,1) + % this relies on the curve fitting toolbox + % sIC(i,:)=(smooth(pIC(i,:),101))'; + sIC(i,:)=ft_preproc_smooth(pIC(i,:), 101); + end + + + %%%%%%%%%% Unworking channel identification %%%%%%%%%%%%%%% + disp('STAGE POST E'); + disp('unworking channel identification...'); + + % declare some variables + ch_ic_list = []; + bad_channels = []; + bad_channels2 = []; + size_fx = size_s(1,3)/3; + size_fy = size_s(1,4)-(size_s(1,4)/5); + border_x = (size_s(1,4)/20); + A = comp_freq.topo; + for i=1:Nc + clear chan_lab; + flag_bad=0; + [vt,order] = sort(abs(A(:,i))); % Sort the channels according to the weight on each IC + vt = flipud(vt); + order = flipud(order); + + if (strcmp(ch_method,'max_weight')) + + if vt(1)/vt(2) > 10 % One channel is discarded if the IC weight is 10 times grater than the others + flag_bad=1; + mth='val'; + end + else + % [histo_ch bins_ch]=hist(abs(A(:,i)),400); + mean_weight=prctile(vt,50,1); + std_weight=std(vt(2:end)); + if vt(1)> mean_weight+threshold_std*std_weight % One channels is discarded if the IC weight is 10 times std out of the mean + h_fig=figure; + % set(h_fig, 'Position', [(size_s(1,3)-(border_x+size_fx)) border_x size_fx size_fy]) + set(h_fig, 'visible', 'off','paperposition', [1 1 10 7]); + [histo_ch,bin_ch]=hist(vt,400); + hist(vt,400); + xlabel('weight value ','color','k'); + title('Mixing Matrix Weight distribution','FontSize',12); + line([mean_weight+10*std_weight mean_weight+10*std_weight], [0 max(histo_ch)],'color','r') + pause(3); + flag_bad=1; + mth='std'; + end + end + if(flag_bad==1) + posi=[border_x border_x size_fx size_fy]; + % options={'plottype','components','component',i,'position',posi}; + % hcp_ICA_plot(comp_freq,options) % summary plots of the IC + if(strcmp(mth,'std')) + n_chan=size(find(vt>mean_weight+10*std_weight),1); + chan_lab(1,1:n_chan) = iteration(1,1).comp.topolabel(order(1:n_chan)); + else + chan_lab = iteration(1,1).comp.topolabel(order(1)); + end + str_chan=char(chan_lab(1,1)); + if(n_chan>1) + for is=2:n_chan + str_chan=[str_chan, '-' ,char(chan_lab(1,is))]; + end + end + + if(strcmp(selmode,'user')) + string_1=['method ', mth , ': Do you want elminiate the channel/s ', str_chan ]; + ButtonName2=questdlg(string_1, ... + 'Question', ... + 'Yes','No','Yes'); + else + ButtonName2='Yes'; + end + if strcmp(ButtonName2,'Yes') + imgname=[resultprefix '_icaqc_badchannel_' str_chan]; + options={'plottype','components','component',i,'saveres','yes','grad', dataraw.grad,'modality',modality,'saveformat','png','fileout',imgname,'visible','off'}; + hcp_ICA_plot(comp_freq,options) % summary plots of the IC + ch_ic_list=[ch_ic_list i]; + chan=strcat('-',chan_lab); + else + chan_lab=[]; + end + else + chan_lab=[]; + end + if(~isempty(chan_lab)) + bad_channels=[bad_channels,chan]; + bad_channels2=[bad_channels2, chan_lab]; + end + close all + end + + if(isempty(bad_channels)) + disp('no channel identified by ICA'); + else + disp('bad channels identified '); + disp(bad_channels2); + end + + if(~isempty(ch_ic_list)) + IC(ch_ic_list,:)=[]; sIC(ch_ic_list,:)=[]; + end + + %%%%%%% Find IC intervals with large artifacts according to a local variance %%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%% The variance of the time course is evaluated in different segments %%%%%%%%%%%%%%% + disp('evaluating IC variances'); + + ic_ind=[]; + [Nc data_length] = size(IC); + bin=round(comp.fsample/decfactor); % width of the segment + nstep=floor(data_length/bin); % number of segments + clear std_IC mean_sIC max_std max_val max_ind + + for i=1:Nc + for j=1:nstep + std_IC(i,j) =std(IC(i,(bin*(j-1)+1):(bin*j))); % standard deviation of the i-th segment + mean_sIC(i,j)=mean(sIC(i,(bin*(j-1)+1):(bin*j))); % mean fo the smoothed pIC + end + max_std(i)=max(std_IC(i,:)); % find the segment with largest standard deviation + [max_val(i),max_ind(i)] = max(abs(IC(i,:))); + end + + for i=1:Nc + [junk c0]=find(std_IC(i,:)==0); + std_IC(:,c0)=[]; % delete sements with zero std + [histo bins]=hist(std_IC(i,:),400); % create an histogram for the std + mean_std=mean(std_IC(i,:)); + std_std=std(std_IC(i,:)); + + if(max_std(i)>(mean_std+(num_sig*std_std/decfactor))) + ic_ind=i; + break + else + ic_ind=0; + end + end + + + %%%%%%%%%%%%% Graphically or automatically select the time intervals to be excluded %%%%%%%%% + int_vect=[]; + while (ic_ind) + disp('performing bad segments selection '); + + ButtonName='No'; + while strcmp(ButtonName,'No') + + % Choosing a window around the selected artifact + offset=10000; + start_point=max_ind(ic_ind)-offset; + end_point=max_ind(ic_ind)+offset; + if(start_point<=0) + start_point=1; + end + if(end_point>=size(IC,2)) + end_point=size(IC,2); + end + IC2=abs(IC(ic_ind,start_point:end_point)); + sIC2=sIC(ic_ind,start_point:end_point); + [junk,max_ind2] = max(abs(IC2(1,:))); + + % Defining the interval to be cutted around the artifact peak by requiring that the + % smooted power is over a given threshold + flag_inf=0; + flag_sup=0; + clear inf_thr sup_thr + ref_sIC = prctile(mean_sIC(ic_ind,:),50,2); % median of the smoothed IC power + for i=1:20 + infseg=max_ind2-i*500; if(infseg<1), infseg=1; end + supseg=max_ind2+i*500; if(supseg>size(IC2,2)), supseg=size(IC2,2); end + % [junk over_thr]=find(sIC2(infseg+1:supseg)>sic_thr_par*ref_sIC); + [junk over_thr]=find(sIC2(infseg:supseg)>sic_thr_par*ref_sIC); + + if(~isempty(over_thr)) + if(flag_inf==0) + inf_thr(i)=min(over_thr)+infseg; + if(i>1) + if(inf_thr(i)==inf_thr(i-1)), flag_inf=1; end + end + end + if(flag_sup==0) + sup_thr(i)=max(over_thr)+infseg; + if(i>1) + if(sup_thr(i)==sup_thr(i-1)), flag_sup=1;end + end + end + else + inf_thr(i)=infseg; + flag_inf=1; + sup_thr(i)=supseg; + flag_sup=1; + end + if(i>1) + if(flag_inf==1 && flag_sup==1) + break + end + end + end + + % Plots the selected artifact interval + disp('plotting bad segments '); + + ymax=max(IC2); + points_all=(1:size(IC,2)); + + cfg=[]; + cfg.component=ic_ind; + cfg.layout=layout; + + h1_fig=figure; + % set(h1_fig, 'Position', [75 75 size_s(1,3)-150 size_s(1,4)-150]) + set(h1_fig, 'visible', 'off','paperposition', [1 1 10 7]); + + subplot(2,3,3) ; plot(points_all,IC(ic_ind,:)) + hold on + plot(points_all(1,start_point:end_point),IC(ic_ind,start_point:end_point),'k') + legend('IC time course','interval around the peak','Location','NorthOutside') + subplot(2,3,4) + ft_topoplotIC(cfg, comp_freq) + subplot(2,3,5); plot(bins,histo) + line([mean_std+10*std_std mean_std+10*std_std], [0 max(histo)],'Color','r') + legend('std distribution','mean and peak thr','Location','NorthOutside') + line([mean_std mean_std], [0 max(histo)],'Color','r') + % subplot(2,3,[1 2]) ; plot(IC2,'k') + subplot(2,3,6) ; plot(sIC2,'k') + line([1 size(IC2,2)], [sic_thr_par*ref_sIC sic_thr_par*ref_sIC],'Color','r') + axis([1 length(sIC2) 0 max(sIC2)]) + subplot(2,3,[1 2]) ; plot(IC2,'k') + hold on + plot(min(inf_thr):min(max(sup_thr),size(IC2,2)),IC2(1,min(inf_thr):min(max(sup_thr),size(IC2,2))),'r') + legend('abs of the IC','suggested cut') + axis([1 length(IC2) 0 ymax]) + title('Select time interval to be excluded...'); + + + % time_points=[1:cut_interval(1) cut_interval(2):length(IC)]; + if(strcmp(selmode,'user')) + + [X,Y] = ginput(2); + cut_interval=round(X)+start_point; + + subplot(2,3,3) ; plot(points_all,IC(ic_ind,points_all)) + hold on + plot(points_all(1,start_point:end_point),IC(ic_ind,start_point:end_point),'k') + plot(points_all(1,cut_interval(1,1):cut_interval(2,1)),IC(ic_ind,cut_interval(1,1):cut_interval(2,1)),'g') + legend('IC time course','interval around the peak','selected interval','Location','NorthOutside') + + ButtonName=questdlg('Are you satisfied with the interval selection?', ... + 'Question', ... + 'Yes','No','skip','Yes'); + else + segcount=segcount+1; + ButtonName='Yes'; + cut_interval=[min(inf_thr)+start_point;max(sup_thr)+start_point]; + imgname=[resultprefix '_icaqc_badsegment_' num2str(segcount)]; + hcp_write_figure(imgname, h1_fig); + end + close(h1_fig); + + end + close all + disp('Done bad segments selection '); + + if(cut_interval(2)-cut_interval(1)<100) + cut_interval(1)=cut_interval(1)-100; + cut_interval(2)=cut_interval(2)+100; + end + if(cut_interval(1)<=0) + cut_interval(1)=1; + end + if(cut_interval(2)>size(IC,2)) + cut_interval(2)=size(IC,2); + end + IC(:,cut_interval(1):cut_interval(2))=0; + sIC(:,cut_interval(1):cut_interval(2))=0; + if(strcmp(ButtonName,'Yes')) + int_vect=[int_vect; cut_interval(1) cut_interval(2)]; + end + + clear std_IC mean_sIC max_std max_val max_ind + for i=1:Nc + for j=1:nstep + std_IC(i,j)=std(IC(i,(bin*(j-1)+1):(bin*j))); + mean_sIC(i,j)=mean(sIC(i,(bin*(j-1)+1):(bin*j))); + end + max_std(i)=max(std_IC(i,:)); + [max_val(i),max_ind(i)] = max(abs(IC(i,:))); + end + + for i=1:Nc + [junk c0]=find(std_IC(i,:)==0); + std_IC(:,c0)=[]; + [histo bins]=hist(std_IC(i,:),400); + mean_std=mean(std_IC(i,:)); + std_std=std(std_IC(i,:)); + + if(max_std(i)>(mean_std+(num_sig*std_std))) + ic_ind=i; + break + else + ic_ind=0; + end + end + + end + disp('Verify the iteration results '); + + %%%%%%%%%%%%% Verify the iteration results %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + flag_int=0; + if(size(int_vect)) + int_vect_time=int_vect/comp_freq.fsample; + skint=vertcat(skint,int_vect_time); + [junk ord]=sort(skint(:,1)); + skint=skint(ord,:); + i=0; + flag_sk=0; + while iskint(i+1,1) && skint(i,2)skint(i+1,1) && skint(i,2)>skint(i+1,2)) + skint(i+1,:)=[]; + flag_sk=1; + break + end + end + end + flag_sk=0; + i=0; + while i. + +% call the old-fashioned way (i.e. also do preprocessing) when first input +% is a string +if ischar(data_meg) + data_meg = hcp_ICA_preprocessing(data_meg, options); +end + +%%%%%%%%% deal with the input options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% get the options +ica_iterations = ft_getopt(options, 'ica_iterations', 1); +saveIC = ft_getopt(options, 'save_comp', 'yes'); +selchan = ft_getopt(options, 'channels', 'MEG'); + +%%%%%%%%% Performing ICA using the FastICA algorithm%%%%%%%%%%%%%%%%%%%%%% + +disp('performing ICA...'); + +cfg = []; +cfg.method = 'fastica'; +cfg.fastica.approach = 'defl'; +cfg.fastica.g ='tanh'; +cfg.fastica.maxNumIterations = 200; +cfg.fastica.numOfIC = size(data_meg.label,1); +cfg.fastica.verbose = 'on'; +cfg.fastica.displayMode = 'off'; +cfg.demean = 'no'; + +for j = 1:ica_iterations + + pack; + randn('state',j); + cfg.fastica.initGuess = randn(numel(data_meg.label)); + + comp = hcp_componentanalysis(cfg, data_meg); + A = comp.topo; + W = comp.unmixing; + + [chan, Nc] = size(A); + + %%% Ordering ICs according to the Power + if Nc > 0 + power = mean(A.^2); + [~, order] = sort(power, 'descend'); + + A = A(:,order); + W = W(order,:); + end + + % copy the stuff back into the output structure + comp.order = order; + comp.topo = A; + comp.unmixing = W; + if strcmp(saveIC,'yes') + for iic=1:size(comp.sampleinfo,1), comp.trial{iic} = comp.trial{iic}(order,:); end + else + comp = rmfield(comp, 'trial'); + comp = rmfield(comp, 'time'); + end + + % put in the output structure + iteration(j).comp = comp; % structure containing all the iterations + + clear comp A W IC power order +end diff --git a/analysis_functions/hcp_anatomy_qualitycheck.m b/analysis_functions/hcp_anatomy_qualitycheck.m new file mode 100644 index 0000000..825fc5b --- /dev/null +++ b/analysis_functions/hcp_anatomy_qualitycheck.m @@ -0,0 +1,153 @@ +function hcp_anatomy_qualitycheck(filename, fiducials, landmarks, transform, mri, headshape, headshapemri, headmodel, sourcemodel2d, sourcemodel3d) + +% HCP_ANATOMY_QUALITYCHECK saves a bunch of figures from the output of +% the anatomy pipeline, which need to be inspected visually in order to +% check the quality of the output +% +% Use as +% hcp_anatomy_qualitycheck(filename) +% +% For example +% hcp_anatomy_qualitycheck('CP10018_anatomy.mat') +% +% See also HCP_ANATOMY_PIPELINE, HCP_ENSURE_UNITS, HCP_ENSURE_COORDSYS + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +close all + +% FIXME this should be assigned in hcp_anatomy_pipeline +fiducials.coordsys = 'vox'; +landmarks.coordsys = 'vox'; +headshape.coordsys = 'bti'; +headshapemri.coordsys = 'bti'; + +% ensure that they all are represented in a consistent coordinate system +mri = hcp_ensure_coordsys(mri, transform, 'bti'); +fiducials = hcp_ensure_coordsys(fiducials, transform, 'bti'); +headmodel = hcp_ensure_coordsys(headmodel, transform, 'bti'); +sourcemodel2d = hcp_ensure_coordsys(sourcemodel2d, transform, 'bti'); +sourcemodel3d = hcp_ensure_coordsys(sourcemodel3d, transform, 'bti'); +headshape = hcp_ensure_coordsys(headshape, transform, 'bti'); +headshapemri = hcp_ensure_coordsys(headshapemri, transform, 'bti'); + +% ensure that they all have consistent units +mri = hcp_ensure_units(mri, 'mm'); +%fiducials = hcp_ensure_units(fiducials, 'mm'); +headmodel = hcp_ensure_units(headmodel, 'mm'); +sourcemodel2d = hcp_ensure_units(sourcemodel2d, 'mm'); +sourcemodel3d = hcp_ensure_units(sourcemodel3d, 'mm'); +headshape = hcp_ensure_units(headshape, 'mm'); +headshapemri = hcp_ensure_units(headshapemri, 'mm'); + +% define some helper functions +viewtop = @() view( 0, 90); +viewbottom = @() view(180, -90); +viewleft = @() view(180, 0); +viewright = @() view( 0, 0); +viewfront = @() view( 90, 0); % FIXME this might also be back + +figure; +subplot(2,2,1); hold on; viewbottom(); ft_plot_vol(headmodel); ft_plot_fiducials(fiducials); +subplot(2,2,2); hold on; viewtop(); ft_plot_vol(headmodel); ft_plot_fiducials(fiducials); +subplot(2,2,3); hold on; viewleft(); ft_plot_vol(headmodel); ft_plot_fiducials(fiducials); +subplot(2,2,4); hold on; viewright(); ft_plot_vol(headmodel); ft_plot_fiducials(fiducials); +axis on; grid on; +hcp_write_figure([filename,'_headmodel.png']); + +figure; +subplot(2,2,1); hold on; viewbottom(); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); ft_plot_fiducials(fiducials); +subplot(2,2,2); hold on; viewtop(); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); ft_plot_fiducials(fiducials); +subplot(2,2,3); hold on; viewfront(); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); ft_plot_fiducials(fiducials); +subplot(2,2,4); hold on; viewright(); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); ft_plot_fiducials(fiducials); +axis on; grid on; +hcp_write_figure([filename,'_sourcemodel_3d.png']); + +figure; +options = {'transform',mri.transform,'intersectmesh',{sourcemodel2d headmodel.bnd}}; +subplot(2,2,1); hold on; ft_plot_slice(mri.anatomy, 'location', [0 0 60], 'orientation', [0 0 1], options{:}); viewtop(); +subplot(2,2,2); hold on; ft_plot_slice(mri.anatomy, 'location', [0 0 20], 'orientation', [0 0 1], options{:}); viewtop(); +subplot(2,2,3); hold on; ft_plot_slice(mri.anatomy, 'location', [0 20 0], 'orientation', [1 0 0], options{:}); viewfront(); +subplot(2,2,4); hold on; ft_plot_slice(mri.anatomy, 'location', [0 20 0], 'orientation', [0 1 0], options{:}); viewright(); +set(gcf, 'Renderer', 'zbuffer') +hcp_write_figure([filename,'_sourcemodel_2d.png']); + +options = {'transform', mri.transform,'nslice',16,'intersectmesh',{sourcemodel2d headmodel.bnd},... + 'intersectlinewidth',1,'slicesize',[300 300]}; + +figure; +ft_plot_montage(mri.anatomy, 'location', [0 0 0], 'orientation', [0 0 1], 'slicerange', [-20 120], options{:}); +set(gcf, 'Renderer', 'zbuffer'); +hcp_write_figure([filename,'_slice1.png'], 'resolution', 500); + +figure; +ft_plot_montage(mri.anatomy, 'location', [0 0 0], 'orientation', [0 1 0], 'slicerange', [-60 60], options{:}); +set(gcf, 'Renderer', 'zbuffer'); +hcp_write_figure([filename,'_slice2.png'], 'resolution', 500); + +figure; +ft_plot_montage(mri.anatomy, 'location', [0 0 0], 'orientation', [1 0 0], 'slicerange', [-70 110], options{:}); +set(gcf, 'Renderer', 'zbuffer'); +hcp_write_figure([filename,'_slice3.png'], 'resolution', 500); + +v = headshapemri.bnd.pnt; +f = headshapemri.bnd.tri; +[f,v]=reducepatch(f,v, 0.2); +headshapemri.bnd.pnt = v; +headshapemri.bnd.tri = f; +figure +subplot(2,2,1);hold on; ft_plot_mesh(headshapemri.bnd,'edgecolor','none','facecolor','b','fidcolor','y'); +ft_plot_headshape(headshape); viewbottom(); +subplot(2,2,2);hold on; ft_plot_mesh(headshapemri.bnd,'edgecolor','none','facecolor','b','fidcolor','y'); +ft_plot_headshape(headshape); viewtop(); +subplot(2,2,3);hold on; ft_plot_mesh(headshapemri.bnd,'edgecolor','none','facecolor','b','fidcolor','y'); +ft_plot_headshape(headshape); viewfront(); +subplot(2,2,4);hold on; ft_plot_mesh(headshapemri.bnd,'edgecolor','none','facecolor','b','fidcolor','y'); +ft_plot_headshape(headshape); viewright(); +axis on; +grid on; +hcp_write_figure([filename,'_headshape.png'], 'resolution', 500); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function writetextfile(filename, str) +fid = fopen(filename, 'wt'); +fwrite(fid, str); +fclose(fid); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function ft_plot_fiducials(fiducials) +x = fiducials.nas(1); +y = fiducials.nas(2); +z = fiducials.nas(3); +plot3(x, y, z, 'b*'); +x = fiducials.lpa(1); +y = fiducials.lpa(2); +z = fiducials.lpa(3); +plot3(x, y, z, 'b*'); +x = fiducials.rpa(1); +y = fiducials.rpa(2); +z = fiducials.rpa(3); +plot3(x, y, z, 'b*'); + + diff --git a/analysis_functions/hcp_arti_usestd.m b/analysis_functions/hcp_arti_usestd.m new file mode 100644 index 0000000..b22e21a --- /dev/null +++ b/analysis_functions/hcp_arti_usestd.m @@ -0,0 +1,74 @@ +function[badTrials,trialsStdMat]=hcp_arti_usestd(inData,stdRatio) + +% This function idenitifies but trials by using the ratio of the std of +% single trials over the std of all trials for each channel. The user +% defines a threshold. For each trials all channels are checked to see if +% the std artio is above the threshold. If at least ONE(this is hard coded. should be dynamic?) +% channel is found aboce the threshold then this trial is assumed bad. +% Please notice that this methid tends to remove trials that have an +% unusually high variance. Channels with persistently high variance might +% escape. +% +% +% INPUTS +% stdRatio: This is the rate of the std of individual trial ./ std of all trials +% per channel used to find bad trials. Trials in this category are checked and +% according to the number of channels for which this rate is higher +% (set below Nchannels4BadTrial), they are marked as bad or not +% +% OUPUTS +% badTrials: The indices of the bad Trials +% stdMat: Matrix Nchannels x Ntrials with the std for each trial and channel + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + + +threshStdRate=stdRatio; + +Nchannels4BadTrial=1; % This is the least number of channels for which a bad trial must have the std ratio higher than the threshold. TODO: make it an input argument ? +%---- +%=========================== + +trialsStd = cellfun(@(x) std(x,0,2), inData.trial, 'UniformOutput', false); +trialsStdMat=[trialsStd{:}]; +% [COEFF, SCORE, LATENT, TSQUARED] = princomp(trialsStdMat); +Nchannels=size(inData.trial{1},1); +NsampPerTrial=size(inData.trial{1},2); +Ntrials=length(inData.trial); +%========================================================= +totalData=[inData.trial{:}]; +stdTotalPerChan=std(totalData,0,2); +%========================================================= +stdMatNorm=trialsStdMat./repmat(stdTotalPerChan,1,Ntrials); +[chanIndx,trlIndx]=find(stdMatNorm>threshStdRate); % TODO: This rate should be dynamically set by the user +[uniqueChan,dummyIndx,uniqueChanMap]=unique(chanIndx); +[uniqueTrial,dummyIndx,uniqueTrialMap]=unique(trlIndx); +NchanUniq=length(uniqueChan); +NtrialUniq=length(uniqueTrial); +perChanCounts=zeros(1,NchanUniq); +perTrialCounts=zeros(1,NtrialUniq); +for iCh=1:NchanUniq, + perChanCounts(iCh)=sum(uniqueChanMap==iCh); +end +for iTrl=1:NtrialUniq, + perTrialCounts(iTrl)=sum(uniqueTrialMap==iTrl); +end +%========================================================= +indx1=find(perTrialCounts>Nchannels4BadTrial); % TODO: This number of Channels in which a trial appears bad should be dynamically set by the user +badTrials=uniqueTrial(indx1); + diff --git a/analysis_functions/hcp_check_pipelineoutput.m b/analysis_functions/hcp_check_pipelineoutput.m new file mode 100644 index 0000000..93272fa --- /dev/null +++ b/analysis_functions/hcp_check_pipelineoutput.m @@ -0,0 +1,360 @@ +function hcp_check_pipelineoutput(pipeline, varargin) + +% HCP_CHECK_PIPELINEOUTPUT checks that the expected output files from a +% certain pipeline exist. The expected files are determined based on the +% documentation on the HCP wiki. The purpose of this script is to call it +% at the end of the respective pipeline, and at the beginning of any other +% pipeline that expects the output of the respective pipeline. +% +% Use as +% hcp_check_pipelineoutput(pipeline, ...) +% where the input is a string with the pipeline name (see below). +% +% The additional input arguments should be specified as key-value pairs and +% may include +% subject = string with subject ID, e.g. CP10128 +% experiment = string with experiment ID, e.g. CP10128_MEG_v2 +% scan = string with scan ID, e.g. 2-Wrkmem_MBP_VG1 +% +% In general for the pipeline described on +% https://wiki.humanconnectome.org/display/EEG/xxxx +% you would call this function as +% hcp_check_pipelineoutput('xxxx', ...) +% +% The pipelines included in this function are documented ar +% https://wiki.humanconnectome.org/display/EEG/ +% +% Depending on the pipeline name, subject, experiment, and scan identifier, +% the expected files according to the documentation are determined and their +% presence is checked. Please refer to the code of this function and the +% online documentation of the wiki for the actual output file names of each +% function. + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% get the optional input arguments +subject = ft_getopt(varargin, 'subject', ''); +experiment = ft_getopt(varargin, 'experiment', ''); +scan = ft_getopt(varargin, 'scan', ''); +band = ft_getopt(varargin, 'band', ''); +net_seed = ft_getopt(varargin, 'net_seed', ''); +freq = ft_getopt(varargin, 'freq', ''); +datagroup = ft_getopt(varargin, 'datagroup', ''); +contrasts = ft_getopt(varargin, 'contrasts', ''); +avgmode = ft_getopt(varargin, 'avgmode', ''); +gridtype = ft_getopt(varargin, 'gridtype', ''); +sourcemodel_type = ft_getopt(varargin, 'sourcemodel', ''); + +switch pipeline + + case 'baddata' + assert_file('%s_%s_baddata_badchannels.txt', experiment, scan); + assert_file('%s_%s_baddata_badsegments.txt', experiment, scan); + assert_file('%s_%s_baddata_manual_badsegments.txt', experiment, scan); + assert_file('%s_%s_baddata_manual_badchannels.txt', experiment, scan); + + assert_file('%s_%s_baddata_badchan_cor_scatter.png', experiment, scan); + assert_file('%s_%s_baddata_badchan_cor_topo3D.png', experiment, scan); + assert_file('%s_%s_baddata_badchan_cor_topo.png', experiment, scan); + assert_file('%s_%s_baddata_badchan_std_scatter.png', experiment, scan); + assert_file('%s_%s_baddata_badchan_std_topo.png', experiment, scan); + + tok=tokenize(scan,'-'); + scanmnem=tok{2}; + isTask=strcmp(scanmnem,'Wrkmem')|strcmp(scanmnem,'Motort')|strcmp(scanmnem,'StoryM'); + if isTask, + assert_file('%s_%s_baddata_rawtrialinfo_QC.txt', experiment, scan); + end + + case 'icaclass' + assert_file('%s_%s_icaclass.mat', experiment, scan); + assert_file('%s_%s_icaclass_1.png', experiment, scan); % there can be more figures, but at least one is expected + + %------------------------------------------------------- + % ---- Check if the data is from MOTOR TASK for which no ECG EOG channels + % are available and construct pseudo ECG EOG channels based on topology + % templates. As we do not use EEG anymore in Phase 2 , MOTOR task data + % should also have EO/CG channels and this part should not be necessary + oldMotorExperimentIds = { + 'CP10128_MEG_v1' % Glasgow scan + 'CP10129_MEG_v1' % Glasgow scan + 'CP10141_MEG_v1' % There are 2 scans with 2 difference EMG electrode locations. For each scan there are 3 blocks for each hand and foot with 12 trials in each block. So each condition has only 36 trials. + 'CP10138_MEG' % There are 6 different scan with variable ISI and time jittering in some. For each scan there are 3 blocks for each hand and foot with beween 8 to 12 trials in each block. So each condition has only 24 to 36 trials. + 'CP10167_MEG' % 1 scan with the latest protocol. 8 blocks per hand and foot with 10 trials each. 1200 msec ISI. However there is no MRI + 'CP10113_MEG_v2' % There are 2 different scans. NOt clear what the difference is between the two. There are 8 blocks for each hand and foot, with 10 trials per block. Unfortunately EMG signal is bad for both Hands and trials cannot be extracted based on the EMG signal + }; + + isMotorTask = ~isempty(regexp(scan,'Motor', 'once')); + isScanWithOldMotor = ismember(experiment, oldMotorExperimentIds); + if isMotorTask && isScanWithOldMotor + assert_file('%s_%s_icaclass_virtchanVEOG.png', experiment, scan); % there can be more figures, but at least one is expected + assert_file('%s_%s_icaclass_virtchanECG.png', experiment, scan); % there can be more figures, but at least one is expected + end + %------------------------------------------------------- + + case 'icaqc' + error('the "icaqc" pipeline has been deprecated, please use the "baddata" pipeline instead'); + + case 'rmegpreproc' + assert_file('%s_%s_rmegpreproc.mat', experiment, scan); + + case 'tmegpreproc' + %---------------- + tmpscan = scan; + isInCell = iscell(tmpscan); + if isInCell, + Nfiles = length(tmpscan); + else + Nfiles = 1; + tmpscan = {tmpscan}; + end + for iScan = 1:Nfiles, + assert_file('%s_%s_tmegpreproc_trialinfo.mat', experiment, tmpscan{iScan}); + end + %---------------- + if ~isempty(datagroup) + tmpgroup = datagroup; + isInCell = iscell(tmpgroup); + if isInCell, + Ngroups = length(tmpgroup); + else + Ngroups = 1; + tmpgroup = {tmpgroup}; + end + %---------------- + for iFile = 1:Nfiles + for iGroup = 1:Ngroups + assert_file('%s_%s_tmegpreproc_%s.mat', experiment, tmpscan{iFile}, tmpgroup{iGroup}); + end + end + else + disp('No datagroup names provided - Checking only for trialinfo information'); + end + %---------------- + + case 'eravg' + %---------------- + tmpscan = scan; + isInCell = iscell(tmpscan); + if isInCell, + Nfiles = length(tmpscan); + else + Nfiles = 1; + tmpscan = {tmpscan}; + end + + %---------------- + tmpcontr = contrasts; + isInCell = iscell(tmpcontr); + if isInCell, + Ncontr = length(tmpcontr); + else + Ncontr = 1; + tmpcontr = {tmpcontr}; + end + %---------------- + tmpavgmode = avgmode; + isInCell = iscell(tmpavgmode); + if isInCell, + Navgmodes = length(tmpavgmode); + else + Navgmodes = 1; + tmpavgmode = {tmpavgmode}; + end + %---------------- + for iFile = 1:Nfiles + for iCntr = 1:Ncontr + for iAvgMode = 1:Navgmodes + if strcmp(tmpavgmode{iAvgMode},'mag') + assert_file('%s_%s_%s_[MODE-mag].mat', experiment, tmpscan{iFile}, tmpcontr{iCntr}); + elseif strcmp(tmpavgmode{iAvgMode},'planar') + assert_file('%s_%s_%s_[MODE-planar].mat', experiment, tmpscan{iFile}, tmpcontr{iCntr}); + end + end + end + end + + case 'tfavg' + %---------------- + tmpscan = scan; + isInCell = iscell(tmpscan); + if isInCell, + Nfiles = length(tmpscan); + else + Nfiles = 1; + tmpscan = {tmpscan}; + end + %---------------- + tmpcontr = contrasts; + isInCell = iscell(tmpcontr); + if isInCell, + Ncontr = length(tmpcontr); + else + Ncontr = 1; + tmpcontr = {tmpcontr}; + end + %---------------- + tmpavgmode = avgmode; + isInCell = iscell(tmpavgmode); + if isInCell, + Navgmodes = length(tmpavgmode); + else + Navgmodes = 1; + tmpavgmode = {tmpavgmode}; + end + %---------------- + for iFile = 1:Nfiles + for iCntr = 1:Ncontr + for iAvgMode = 1:Navgmodes + if strcmp(tmpavgmode{iAvgMode},'mag') + assert_file('%s_%s_%s_[MODE-mag].mat', experiment, tmpscan{iFile}, tmpcontr{iCntr}); + elseif strcmp(tmpavgmode{iAvgMode},'planar') + assert_file('%s_%s_%s_[MODE-planar].mat', experiment, tmpscan{iFile}, tmpcontr{iCntr}); + end + end + end + end + + case 'tfsens' + %---------------- + tmpscan = scan; + isInCell = iscell(tmpscan); + if isInCell, + Nfiles = length(tmpscan); + else + Nfiles = 1; + tmpscan = {tmpscan}; + end + %---------------- + tmpgroup = datagroup; + isInCell = iscell(tmpgroup); + if isInCell, + Ngroups = length(tmpgroup); + else + Ngroups = 1; + tmpgroup = {tmpgroup}; + end + %---------------- + tmpband = band; + isInCell = iscell(tmpband); + if isInCell, + Nbands = length(tmpband); + else + Nbands = 1; + tmpband = {tmpband}; + end + %---------------- + for iFile = 1:Nfiles + for iGroup = 1:Ngroups + for iBand = 1:Nbands + assert_file('%s_%s_tfsens_%s_%s.mat', experiment, tmpscan{iFile}, tmpgroup{iGroup}, tmpband{iBand}); + end + end + end + + case 'srcavglcmv' + %---------------- + tmpscan = scan; + isInCell = iscell(tmpscan); + if isInCell, + Nfiles = length(tmpscan); + else + Nfiles = 1; + tmpscan = {tmpscan}; + end + %---------------- + tmpcontr = contrasts; + isInCell = iscell(tmpcontr); + if isInCell, + Ncontr = length(tmpcontr); + else + Ncontr = 1; + tmpcontr = {tmpcontr}; + end + %---------------- + for iFile = 1:Nfiles + for iCntr = 1:Ncontr + assert_file('%s_%s_%s_%s.mat', experiment, tmpscan{iFile}, tmpcontr{iCntr},gridtype); + end + end + + case 'anatomy' + assert_file('%s_anatomy_anatomical.nii', subject); + assert_file('%s_anatomy_fiducials.txt', subject); + assert_file('%s_anatomy_landmarks.txt', subject); + assert_file('%s_anatomy_transform.txt', subject); + assert_file('%s_anatomy_sourcemodel2d.mat', subject); + assert_file('%s_anatomy_sourcemodel3d4mm.mat', subject); + assert_file('%s_anatomy_sourcemodel3d6mm.mat', subject); + assert_file('%s_anatomy_sourcemodel3d8mm.mat', subject); + assert_file('%s_anatomy_headshape.mat', subject); + assert_file('%s_anatomy_headshapemri.mat', subject); + assert_file('%s_anatomy_headmodel.mat', subject); + assert_file('%s_anatomy_headmodel.png', subject); + assert_file('%s_anatomy_sourcemodel_2d.png', subject); + assert_file('%s_anatomy_sourcemodel_3d.png', subject); + assert_file('%s_anatomy_slice1.png', subject); + assert_file('%s_anatomy_slice2.png', subject); + assert_file('%s_anatomy_slice3.png', subject); + assert_file('%s_anatomy_headshape.png', subject); + + case 'datacheck' + assert_file('%s_%s_datacheck_info.txt', experiment, scan); + % assert_file('%s_%s_datacheck_headshape.png', experiment, scan); % this one does not exist for empty room recordings + assert_file('%s_%s_datacheck_jumps.png', experiment, scan); + assert_file('%s_%s_datacheck_MEG_lowfreq_power.png', experiment, scan); + assert_file('%s_%s_datacheck_MEG_powerline_noise.png',experiment, scan); + assert_file('%s_%s_datacheck_MEG_powspctrm.png', experiment, scan); + % assert_file('%s_%s_datacheck_EEG_powspctrm.png', experiment, scan); % not relevant any more in the Phase 2 protocol + assert_file('%s_%s_datacheck_MEGREF_powspctrm.png', experiment, scan); + assert_file('%s_%s_datacheck_neighb_correlation.png', experiment, scan); + + case 'icamne' + assert_file('%s_%s_icamne_%s.mat', experiment, scan, sourcemodel_type); + assert_file('%s_%s_icamne_%s_1.png', experiment, scan, sourcemodel_type);% there can be more figures, but at least one is expected + + case 'icaimagcoh' + assert_file('%s_%s_icaimagcoh_%s_freq%s.mat', experiment, scan, sourcemodel_type, freq); + + case 'icapowenv' + assert_file('%s_%s_blp_%s_%s.mat', experiment, scan, sourcemodel_type, band); + + case 'icamcw' + assert_file('%s_%s_mcw_%s_%s_%s.mat', experiment, scan, sourcemodel_type, net_seed, band); + + case 'powavg' + assert_file('%s_%s_powavg.mat', experiment, scan); + assert_file('%s_%s_powavg_multiplot.png', experiment, scan); + assert_file('%s_%s_powavg_singleplot.png', experiment, scan); + + otherwise + error('unknown pipeline "%s"', pipeline); + +end % switch pipeline + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% HELPER FUNCTION to keep the code above more clean +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function assert_file(varargin) +% the following will check for the file being present anywhere on the path +filename = sprintf(varargin{:}); +if ~exist(filename, 'file') + error('the file "%s" does not exist', filename); +else + fprintf('found "%s"\n', filename); +end diff --git a/analysis_functions/hcp_componentanalysis.m b/analysis_functions/hcp_componentanalysis.m new file mode 100644 index 0000000..931fe95 --- /dev/null +++ b/analysis_functions/hcp_componentanalysis.m @@ -0,0 +1,59 @@ +function comp = hcp_componentanalysis(cfg, data) + +% HCP_COMPONENTANALYSIS performs a fastica decomposition of the +% input data using the FT_COMPONENTANALYSIS. In case fastica fails +% to converge for the first component, it will restart. +% +% Use as +% comp = hcp_componentanalysis(cfg, data) +% where +% cfg.method = string describing the method (default = 'fastica') +% cfg.restart = how many times to restart the decomposition in case of convergence problems (default = 10) +% +% See FT_COMPONENTANALYSIS for the other options +% +% See also http://bugzilla.fcdonders.nl/show_bug.cgi?id=1519 + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% get the defaults +cfg.method = ft_getopt(cfg, 'method', 'fastica'); +cfg.restart = ft_getopt(cfg, 'restart', 10); + +% separate the options for this function and the ft function +restart = cfg.restart; +tmpcfg = rmfield(cfg, 'restart'); + +while restart>0 + try + comp = ft_componentanalysis(tmpcfg, data); + restart = 0; + catch + disp(lasterr); + restart = restart - 1; + + randn('state',restart*100); + tmpcfg.fastica.initGuess = randn(numel(data.label)); + + if restart>0 + warning('problem with convergence, restarting the decomposiiton'); + else + error('problem with convergence, not restarting any more'); + end + end % try +end % while diff --git a/analysis_functions/hcp_ensure_coordsys.m b/analysis_functions/hcp_ensure_coordsys.m new file mode 100644 index 0000000..c2d8947 --- /dev/null +++ b/analysis_functions/hcp_ensure_coordsys.m @@ -0,0 +1,31 @@ +function object = hcp_ensure_coordsys(object, transform, desired) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +if ~isfield(object, 'coordsys') + % this one returns an updated structure + object = ft_determine_coordsys(object); +end + +if strcmp(object.coordsys, desired) + % nothing to do +else + T = transform.(sprintf('%s2%s', object.coordsys, desired)); + object = ft_transform_geometry(T, object); + object.coordsys = desired; +end diff --git a/analysis_functions/hcp_ensure_units.m b/analysis_functions/hcp_ensure_units.m new file mode 100644 index 0000000..5507b7f --- /dev/null +++ b/analysis_functions/hcp_ensure_units.m @@ -0,0 +1,29 @@ +function object = hcp_ensure_units(object, desired) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +if ~isfield(object, 'unit') + % this one returns a string + object = ft_convert_units(object); +end + +if strcmp(object.unit, desired) + % nothing to do +else + object = ft_convert_units(object, desired); +end diff --git a/analysis_functions/hcp_eravg_contrasts.m b/analysis_functions/hcp_eravg_contrasts.m new file mode 100644 index 0000000..d150d67 --- /dev/null +++ b/analysis_functions/hcp_eravg_contrasts.m @@ -0,0 +1,652 @@ +function [outStatus] = hcp_eravg_contrasts(inCfg) + +% This function loads time frequency data and computes its trial average as well +% as the average of its planar gradient +% outdatafile is the file where the averaged data will be saved +% outinfofile is the ZIP file where any plotted figures will be saved + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +outStatus=-1; + + +%================================================ +% Get inputs +subjectid=inCfg.subjectid; +experimentid=inCfg.experimentid; +multiscanid=inCfg.multiscanid; % cell with the scan id of both files. They shoudl be in scannning order +contrastlist=inCfg.contrastlist; + + +%================================================ +% Get basic information about input contrasts +Nfiles=length(contrastlist); % 1 (Motor) or 2(WM and SM) files +Ncontr=length(contrastlist{1}); % Check how many contrasts from the first file's contrasts + + +contrnames=[]; +datagroups=[]; +for iC=1:Ncontr, + contrnames{iC}=contrastlist{1}{iC}.mnemprint; + datagroups=unique([datagroups,contrastlist{1}{iC}.lockmode]); +end + +Ngroups=length(datagroups); + +%============================================================= +% Sort contralist list so that contrasts with the same datagroup of the +% first condition are in sequence. This is to avoid reloading data files +% all the time +sortcontrlist=[]; +countcontr=1; +for iGroup=1:Ngroups + for iC=1:Ncontr, + if strcmp(contrastlist{1}{iC}.lockmode{1},datagroups{iGroup}), + sortcontrlist{1}{countcontr}=contrastlist{1}{iC}; + if Nfiles==2 + sortcontrlist{2}{countcontr}=contrastlist{2}{iC}; + end + countcontr=countcontr+1; + end + end +end +sortcontrnames=[]; +sortgroupnames=[]; +isCompCntr=[]; % It says if it is a comparison between conditions +isDifGroups=[]; % It says if there are 2 different groups in each contrast +for iC=1:Ncontr, + sortcontrnames{iC}=sortcontrlist{1}{iC}.mnemprint; + sortgroupnames{iC}=sortcontrlist{1}{iC}.lockmode; + isCompCntr(iC,1)=length(sortgroupnames{iC})-1; + isDifGroups(iC,1)=length(unique(sortgroupnames{iC}))-1; +end +if Nfiles==2 + is2Files=1; +else + is2Files=0; +end +%============================================================= +%============================================================= +%============================================================= +%% LOOP OVER CONTRASTS +prevGroups={'' ''}; +for iC=1:Ncontr + %curcont= + + tmpTok=tokenize(multiscanid{1},'-'); + scanmnem=tmpTok{2}; + %------- The following is just for the cases where the suffix "_Run1 or + %Run2" has been added to the scanid in order to differentiate between 2 + %different runs of the same paradigm. i.e. The way Robert has saved data in + %his database for subject CP10168. + indRunStr=regexp(scanmnem,'_Run'); + if ~isempty(indRunStr), + scanmnem=scanmnem(1:indRunStr(1)-1); + end + + + %================================== + % Load the time data for the corresponding group and from both + % files + + NcurGroups=length(sortgroupnames{iC}); + prevMatchIndx=cell(1,NcurGroups); + for iGr1=1:NcurGroups, + tmpIndx=find(strcmp(sortgroupnames{iC},prevGroups)); + if ~isempty(tmpIndx) + prevMatchIndx{iGr1}=tmpIndx(1); + else + prevMatchIndx{iGr1}=[]; + end + end + if ~isempty(prevMatchIndx{1}) + eval(['tmpdata1A=alldata',num2str(prevMatchIndx{1}),'A;']); + eval(['tmpdata1B=alldata',num2str(prevMatchIndx{1}),'B;']); + else + tmpdata1A=[]; + tmpdata1B=[]; + end + if NcurGroups>1 + if ~isempty(prevMatchIndx{2}) + eval(['tmpdata2A=alldata',num2str(prevMatchIndx{2}),'A;']); + eval(['tmpdata2B=alldata',num2str(prevMatchIndx{2}),'B;']); + else + tmpdata2A=[]; + tmpdata2B=[]; + end + end + + + if isempty(tmpdata1A) + genprefix = sprintf('%s_%s', experimentid, multiscanid{1}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{1}]; + hcp_read_matlab(tmpdatafile,'data'); + alldata1A=data; + + if iC==1, + grad=data.grad; + cfg=[]; + cfg.method='distance'; + cfg.neighbourdist = 0.037; % (3.7 cm is the distance to 1st order neighbs) + neighbours = ft_prepare_neighbours(cfg, data); + end + clear data; + + if is2Files + genprefix = sprintf('%s_%s', experimentid, multiscanid{2}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{1}]; + hcp_read_matlab(tmpdatafile,'data'); + alldata1B=data; clear data; + else + alldata1B=[]; + end + else + alldata1A=tmpdata1A;clear tmpdata1A; + alldata1B=tmpdata1B;clear tmpdata1B; + end + + if isCompCntr(iC) + if isDifGroups(iC) + + if isempty(tmpdata2A) + genprefix = sprintf('%s_%s', experimentid, multiscanid{1}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{2}]; + hcp_read_matlab(tmpdatafile,'data'); + alldata2A=data; clear data; + if is2Files + genprefix = sprintf('%s_%s', experimentid, multiscanid{2}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{2}]; + hcp_read_matlab(tmpdatafile,'data'); + alldata2B=data;clear data; + else + alldata2B=[]; + end + else + alldata2A=tmpdata2A;clear tmpdata2A; + alldata2B=tmpdata2B;clear tmpdata2B; + end + + else + alldata2A=alldata1A; clear tmpdata2A; + alldata2B=alldata1B; clear tmpdata2B; + end + else + alldata2A=[]; + alldata2B=[]; + end + + %------------------------------------------------------ + % Get data from constrast conditions and merge from different files + + + + sel=sortcontrlist{1}{iC}.selection{1}; + if isempty(sel), + datacntr1A=[]; + else + datacntr1A=ft_selectdata(alldata1A,'rpt',sel); + end + + if is2Files, + sel=sortcontrlist{2}{iC}.selection{1}; + if ~isempty(sel) + datacntr1B=ft_selectdata(alldata1B,'rpt',sel); + if ~isempty(datacntr1A) + cfg=[]; % + [indChA,indChB]=match_str(datacntr1A.label,datacntr1B.label); datacntr1A=ft_selectdata(datacntr1A,'channel',indChA); datacntr1B=ft_selectdata(datacntr1B,'channel',indChB); + datacntr1=ft_appenddata(cfg,datacntr1A,datacntr1B); clear datacntr1A datacntr2A + else + datacntr1=datacntr1B; clear datacntr1B; + end + else + datacntr1=datacntr1A; clear datacntr1A; + end + else + datacntr1=datacntr1A; clear datacntr1A; + end + + %-------- + if isempty(datacntr1) + errorFname=['NOTRIALSFOUND_',experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,'].txt']; + errorTime=clock; + errorCase=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + errorType=['No trials were found for condition 1']; + hcp_write_ascii(errorFname,'errorTime','errorCase','errorType'); + warning(['No trial found for condition 1 in ',experimentid,' - ',sortcontrlist{1}{iC}.mnemprint]) ; + continue + end + %-------- + + if isCompCntr(iC) + sel=sortcontrlist{1}{iC}.selection{2}; + if isempty(sel), + datacntr2A=[]; + else + datacntr2A=ft_selectdata(alldata2A,'rpt',sel); + end; + if is2Files, + sel=sortcontrlist{2}{iC}.selection{2}; + if ~isempty(sel), + datacntr2B=ft_selectdata(alldata2B,'rpt',sel); + if ~isempty(datacntr2A) + cfg=[]; % + [indChA,indChB]=match_str(datacntr2A.label,datacntr2B.label); datacntr2A=ft_selectdata(datacntr2A,'channel',indChA); datacntr2B=ft_selectdata(datacntr2B,'channel',indChB); + datacntr2=ft_appenddata(cfg,datacntr2A,datacntr2B); clear datacntr2A datacntr2B + else + datacntr2=datacntr2B; clear datacntr2B; + end + else + datacntr2=datacntr2A; clear datacntr2A; + end + else + datacntr2=datacntr2A; clear datacntr2A; + end + + + %-------- + if isempty(datacntr2) + errorFname=['NOTRIALSFOUND_',experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,'].txt']; + errorTime=clock; + errorCase=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + errorType=['No trials were found for condition 2']; + hcp_write_ascii(errorFname,'errorTime','errorCase','errorType'); + warning(['No trial found for condition 2 in ',experimentid,' - ',sortcontrlist{1}{iC}.mnemprint]) ; + continue + end + %-------- + + end + %------------------------------------------------------ + %==================================================== + %==================================================== + %==================================================== + % DO basic filtering + + flowcfg=[]; + flowcfg.hpfilter='yes'; + flowcfg.hpfreq=[1]; + flowcfg.hpfiltord=4; + + fhighcfg=[]; + fhighcfg.lpfilter='yes'; + fhighcfg.lpfreq=[40]; + + + datacntr1=ft_preprocessing(flowcfg,datacntr1); + datacntr1=ft_preprocessing(fhighcfg,datacntr1); + if isCompCntr(iC) + datacntr2=ft_preprocessing(flowcfg,datacntr2); + datacntr2=ft_preprocessing(fhighcfg,datacntr2); + end + + %========================================================= + % Demean + baseline1=sortcontrlist{1}{iC}.baseline{1}; + dmcfg=[]; + dmcfg.demean='yes'; + dmcfg.baselinewindow=baseline1; + datacntr1=ft_preprocessing(dmcfg,datacntr1); + if isCompCntr(iC) + baseline2=sortcontrlist{1}{iC}.baseline{2}; + dmcfg=[]; + dmcfg.demean='yes'; + dmcfg.baselinewindow=baseline2; + datacntr2=ft_preprocessing(dmcfg,datacntr2); + end + %========================================================= + % Average + avg1=ft_timelockanalysis([],datacntr1); clear datacntr1; + if isCompCntr(iC) + avg2=ft_timelockanalysis([],datacntr2); clear datacntr2; + end + + + + cfg = []; + cfg.feedback='no'; + %cfg.planarmethod = 'sincos'; + cfg.neighbours= neighbours; + avg1.grad=grad; + tmpplanar = ft_megplanar(cfg, avg1); + + combCfg=[]; + %combCfg.combinemethod='svd'; + avg1planar=ft_combineplanar(combCfg, tmpplanar); + if isCompCntr(iC) + cfg = []; + cfg.feedback='no'; + %cfg.planarmethod = 'sincos'; + cfg.neighbours= neighbours; + avg2.grad=grad; + tmpplanar = ft_megplanar(cfg, avg2); + + combCfg=[]; + %combCfg.combinemethod='svd'; + avg2planar=ft_combineplanar(combCfg, tmpplanar); + end + + %==================================================== + %==================================================== + %==================================================== + + %------------------------------------------------------ + % Apply contrast comparison conditions + % --- Check Time + timeperiods=sortcontrlist{1}{iC}.timeperiods{1}; + if ~isempty(timeperiods) + if size(timeperiods,2)==2, + isTimeWin=1; + timepoints=mean(timeperiods,2)'; + else + isTimeWin=0; + timepoints=timeperiods'; + end + Ntpts=size(timeperiods,1); + else + isTimeWin=0; + timepoints=[]; % Leave empty as timeperiods to signify that the original time axis must be kept + Ntpts=length(avg1.time); + end + + if (isTimeWin)&(Ntpts==1) + isSingleWin=1; + elseif (isTimeWin)&(Ntpts>1) + isSingleWin=0; + error('eravg for a time structure of multiple time windows is not yet supported. Put just one time window that sets the time limits or leave empty to get all '); + else + isSingleWin=0; + end + + %--------------- + + if isSingleWin + avg1=ft_selectdata(avg1,'toilim',timeperiods); + avg1planar=ft_selectdata(avg1planar,'toilim',timeperiods); + if isCompCntr(iC) + avg2=ft_selectdata(avg2,'toilim',timeperiods); + avg2planar=ft_selectdata(avg2planar,'toilim',timeperiods); + end + elseif isempty(timeperiods) % original + disp('Just using the original time axis'); + + else + %indtimeord=match_str(tokenize(avg1.dimord,'_'),'time'); + Ndattimes=length(avg1.time); + tmpIndMat=[]; + for iTime=1:Ntpts, + tmpIndMat(iTime)=nearest( avg1.time,timepoints(iTime)); + end + tmpIndMat=unique(tmpIndMat); + avg1.time=avg1.time(tmpIndMat); + avg1.avg=avg1.avg(:,tmpIndMat); + avg1planar.time=avg1planar.time(tmpIndMat); + avg1planar.avg=avg1planar.avg(:,tmpIndMat); + + if isCompCntr(iC) + avg2.time=avg2.time(tmpIndMat); + avg2.avg=avg2.avg(:,tmpIndMat); + avg2planar.time=avg2planar.time(tmpIndMat); + avg2planar.avg=avg2planar.avg(:,tmpIndMat); + + end + + end + %------------------------------------------------------ + %==================================================================== + operation=sortcontrlist{1}{iC}.operation; + if isCompCntr(iC) + if strcmp(operation,'diff') + avg1.avg=avg1.avg-avg2.avg; + avg1planar.avg=avg1planar.avg-avg2planar.avg; + else + error('For eravg comparisons between conditions the supported operation is diff') + end + end + + %-------------------------------------------------------- + + % PLOT + + avg2plot=avg1; + avgmode='mag'; + saveFnameData=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + saveFnameImage=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']_plot.png']; + plotsensmainareas(sortcontrlist{1}{iC},avg2plot,experimentid,scanmnem,saveFnameImage,avgmode); + data=avg1; + hcp_write_matlab(saveFnameData,'data'); clear data; + + avg2plot=avg1planar; + avgmode='planar'; + saveFnameData=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + saveFnameImage=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']_plot.png']; + plotsensmainareas(sortcontrlist{1}{iC},avg2plot,experimentid,scanmnem,saveFnameImage,avgmode); + data=avg1planar; + hcp_write_matlab(saveFnameData,'data'); clear data; + + + prevGroups=sortgroupnames{iC}; + + + +end + + + +end +%--- END OF MAIN -------------------------- +%===================================================== +%===================================================== +%===================================================== + +%% +function[]=plotsensmainareas(incontrast,inavg,experimentid,scanmnem,saveFileName,avgmode) + +channels=[]; +channels.occ = {'A136', 'A137', 'A138', 'A163', 'A164', 'A165', 'A166', 'A185', 'A186', 'A187', 'A203', 'A204', 'A205', 'A219', 'A220', 'A221', 'A238', 'A239'}; +channels.occ_L = {'A134', 'A161', 'A182', 'A183', 'A199', 'A200', 'A201', 'A216', 'A217', 'A218', 'A235'}; +channels.occ_R = {'A107', 'A139', 'A140', 'A166', 'A167', 'A168', 'A188', 'A189', 'A190', 'A205', 'A206', 'A207', 'A222', 'A223'}; +channels.temppf_L = {'A96', 'A97', 'A98', 'A127', 'A128', 'A129', 'A130', 'A155', 'A156', 'A157', 'A179', 'A196'}; +channels.temppf_R = {'A112', 'A113', 'A144', 'A145', 'A146', 'A147', 'A148', 'A171', 'A172', 'A173', 'A174', 'A193', 'A209', 'A210', 'A211', 'A227', 'A247'}; +channels.front = {'A37', 'A38', 'A60', 'A61', 'A62', 'A63', 'A87', 'A88', 'A89', 'A90', 'A91', 'A92', 'A117', 'A118', 'A119', 'A120', 'A123', 'A124', 'A149', 'A150', 'A151'}; +channels.motor_L = {'A7', 'A9', 'A10', 'A11', 'A22', 'A23', 'A24', 'A25', 'A26', 'A41', 'A42', 'A43', 'A44', 'A45', 'A66', 'A67', 'A68', 'A69', 'A70'}; +channels.motor_R ={'A13', 'A14', 'A15', 'A16', 'A17', 'A30', 'A31', 'A32', 'A33', 'A34', 'A53', 'A54', 'A55', 'A56', 'A57', 'A80', 'A81', 'A82', 'A83', 'A84'}; +channels.pariet= {'A49', 'A73', 'A74', 'A75', 'A76', 'A77', 'A102', 'A103', 'A104', 'A105', 'A106', 'A107', 'A134', 'A135', 'A139', 'A162'}; + + + + +%========================================================================== +%========================================================================== +% Make figure outline +h1=figure(); +set(h1,'papertype','A3'); +set(h1,'paperunits','centimeters') +papersize=get(h1,'PaperSize'); +paperposition = [1 1 papersize-1 ]; + +bigFonts1=floor(min(papersize)*0.25); +bigFonts2=floor(min(papersize)*0.5); +bigFonts3=floor(min(papersize)*1); +bigFonts4=floor(min(papersize)*2); + +set(h1,'papersize',papersize); +set(h1,'paperposition',paperposition); +set(h1,'position',[10 10 15*papersize]); +%--------------------------------------------- +Aha(1,1)=axes(); +set(Aha(1,1),'position',[0.1 0.625 0.2 0.1]); +Aha(1,2)=axes(); +set(Aha(1,2),'position',[0.375 0.625 0.2 0.1]); +Aha(1,3)=axes(); +set(Aha(1,3),'position',[0.65 0.625 0.2 0.1]); +%---- +Aha(2,1)=axes(); +set(Aha(2,1),'position',[0.1 0.475 0.2 0.1]); +Aha(2,2)=axes(); +set(Aha(2,2),'position',[0.375 0.475 0.2 0.1]); +Aha(2,3)=axes(); +set(Aha(2,3),'position',[0.65 0.475 0.2 0.1]); +%---- +Aha(3,1)=axes(); +set(Aha(3,1),'position',[0.1 0.325 0.2 0.1]); +Aha(3,2)=axes(); +set(Aha(3,2),'position',[0.375 0.325 0.2 0.1]); +Aha(3,3)=axes(); +set(Aha(3,3),'position',[0.65 0.325 0.2 0.1]); +%======================================================= +%--------------------------------------------- +Bha(1)=axes(); +set(Bha(1),'position',[0.05 0.15 0.15 0.10],'YTick',[],'XTick',[]); +Bha(2)=axes(); +set(Bha(2),'position',[0.3 0.15 0.15 0.10],'YTick',[],'XTick',[]); +Bha(3)=axes(); +set(Bha(3),'position',[0.55 0.15 0.15 0.10],'YTick',[],'XTick',[]); +Bha(4)=axes(); +set(Bha(4),'position',[0.8 0.15 0.15 0.10],'YTick',[],'XTick',[]); +%---- + +Bha(5)=axes(); +set(Bha(5),'position',[0.05 0.01 0.15 0.10],'YTick',[],'XTick',[]); +Bha(6)=axes(); +set(Bha(6),'position',[0.3 0.01 0.15 0.10],'YTick',[],'XTick',[]); +Bha(7)=axes(); +set(Bha(7),'position',[0.55 0.01 0.15 0.10],'YTick',[],'XTick',[]); +Bha(8)=axes(); +set(Bha(8),'position',[0.8 0.01 0.15 0.10],'YTick',[],'XTick',[]); +%---- +%======================================================= +%======================================================= +%======================================================= +%plot +%---- +axes(Aha(1,1)); +tmpavg=ft_selectdata(inavg,'channel',channels.temppf_L,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Left Temp-PF sens','fontsize',bigFonts2); +%---- +axes(Aha(1,2)); +tmpavg=ft_selectdata(inavg,'channel',channels.front,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Frontal sens','fontsize',bigFonts2); +%---- +axes(Aha(1,3)); +tmpavg=ft_selectdata(inavg,'channel',channels.temppf_R,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Right Temp-PF sens','fontsize',bigFonts2); +%--------------------------------------------------- +axes(Aha(2,1)); +tmpavg=ft_selectdata(inavg,'channel',channels.motor_L,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Left Motor sens','fontsize',bigFonts2); +%---- +axes(Aha(2,2)); +tmpavg=ft_selectdata(inavg,'channel',channels.pariet,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Parietal sens','fontsize',bigFonts2); +%---- +axes(Aha(2,3)); +tmpavg=ft_selectdata(inavg,'channel',channels.motor_R,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Right Motor sens','fontsize',bigFonts2); +%--------------------------------------------------- +axes(Aha(3,1)); +tmpavg=ft_selectdata(inavg,'channel',channels.occ_L,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Left Occ. sens','fontsize',bigFonts2); +%---- +axes(Aha(3,2)); +tmpavg=ft_selectdata(inavg,'channel',channels.occ,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Med. Occ. sens','fontsize',bigFonts2); +%---- +axes(Aha(3,3)); +tmpavg=ft_selectdata(inavg,'channel',channels.occ_R,'avgoverchan','yes');colorbar off; +plotCfg=[]; plotCfg.layout='4D248.mat'; ft_singleplotER(plotCfg, tmpavg);colorbar off; +title('ER avg of Right Occ. sens','fontsize',bigFonts2); +%---- +%--------------------------------------------------- +%--------------------------------------------------- +%--------------------------------------------------- +timLim=inavg.time([1 end]); + +tmpTopotimes=[timLim(1):(diff(timLim)./8):timLim(end)]; +tmpStartTimes =tmpTopotimes(1:end-1); +tmpEndTimes =tmpTopotimes(2:end); + +format short g +for iPer=1:length(tmpStartTimes) + iPer + axes(Bha(iPer)); + tmptoilim=[tmpStartTimes(iPer) tmpEndTimes(iPer)]; + tmpavg=ft_selectdata(inavg,'toilim',tmptoilim);colorbar off; + %tmpavg=ft_selectdata(tmpavg,'avgovertime','yes'); + %minVal=min(tmpavg.avg(:)); maxVal=max(tmpavg.avg(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end + + plotCfg=[]; + plotCfg.layout='4D248.mat'; + plotCfg.interpmethod='linear'; + plotCfg.marker='on'; + plotCfg.markersymbol='.'; + plotCfg.gridscale=50; + plotCfg.shading='flat'; + plotCfg.comment='no'; + + %plotCfg.zlim=topozlim; + ft_topoplotER(plotCfg, tmpavg); + colorbar off; + titlStr=sprintf('%2.3f to %2.3f',tmptoilim(1),tmptoilim(2)); + title(titlStr,'fontsize',bigFonts2); +end +%{ +dispStr=sprintf('Bands: \n%s',bandStr); + +txtBoxBand = uicontrol('style','listbox'); +set(txtBoxBand,'units','normalized') +set(txtBoxBand,'Position',[0.75 0.01 0.2 0.25]); +set(txtBoxBand,'String',dispStr) +set(txtBoxBand,'Fontsize',6) +set(txtBoxBand,'HorizontalAlignment','left') + +%------------------------------------------------------------------------- +hcp_write_figure('testTFmulti1.png', h1, 'resolution', 600); + + + +Bha(8)=axes(); +set(Bha(8),'position',[0.575 0.01 0.15 0.10],'YTick',[],'XTick',[]); +%} +%{ +dispStr=sprintf('Bands: \n%s',bandStr); +txtH=axes();axis off; +set(txtH,'position',[0.75 0.01 0.24 0.1],'YTick',[],'XTick',[]); +xlim=get(txtH,'Xlim');ylim=get(txtH,'Ylim'); +ht1=text(xlim(1),ylim(2),dispStr); +set(ht1,'Fontsize',20) +%} +Toph1=axes(); +set(Toph1,'position',[0.1 0.95 0.8 0.05]);axis off +dispStringTop1=sprintf('%s\n%s\n%s',[' experimentid: ',regexprep(experimentid,'_','\\_'),' scan: ',regexprep(scanmnem,'_','\\_')],[' contrast: ',regexprep(incontrast.mnemprint,'_','\\_')],[' avgmode: ',regexprep(avgmode,'_','\\_')]); +htTop1=text(0,0.2,dispStringTop1); +set(htTop1,'Fontsize',bigFonts2) +%{ +Toph2=axes(); +set(Toph2,'position',[0.1 0.775 0.4 0.15]); axis off; +htTop2=text(0,0,[' contrast:',incontrast.mnemprint]); +%} +disp('Saving the figure'); +hcp_write_figure(saveFileName, h1,'format','png'); +disp('Done Saving'); +close(h1); +%========================================================================== +%========================================================================== +end diff --git a/analysis_functions/hcp_exgmontage.m b/analysis_functions/hcp_exgmontage.m new file mode 100644 index 0000000..d382bfe --- /dev/null +++ b/analysis_functions/hcp_exgmontage.m @@ -0,0 +1,147 @@ +function [montage] = hcp_exgmontage(subjectid, experimentid, scanid) + +% HCP_EXGMONTAGE produces a montage for the EEG channel data. +% +% Use as +% montage = hcp_exgmontage(subjectid) +% or +% montage = hcp_exgmontage(subjectid, experimentid, scanid) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +if nargin < 3 + % let the sessions with cap prevail + scanid = '_B'; +end + +if ~isempty(strfind(scanid, 'Rnoise')) || ~isempty(strfind(scanid, 'Pnoise')) + montage = []; +else + + switch subjectid + case {'CP10018', 'CP10019', 'CP10033', 'CP10053', 'CP10066', 'CP10141', 'CP10138', 'CP10167'} + % scans without EEG cap ('CP10066' has unuseful EEG data) + montage = []; + montage.labelorg = { 'E29' 'E30' 'E31' 'E32'}'; + montage.labelnew = {'HEOG' 'ECG' 'VEOG'}'; + montage.tra = [1 -1 0 0; 0 0 1 0; 0 0 0 1]; + + case 'CP10040' + % scans with EEG cap + montage = []; + montage.labelorg = {'E31' 'E32' 'E64'}'; + montage.labelnew = {'ECG' 'VEOG' 'HEOG'}'; + montage.tra = eye(3); + + case {'CP10106'} + % scans with EEG cap + montage = []; + montage.labelorg = { 'E29' 'E30' 'E31' 'E32'}'; + montage.labelnew = {'HEOG' 'ECG' 'VEOG'}'; + montage.tra = [1 -1 0 0; 0 0 1 0; 0 0 0 1]; + + case {'CP10113'} + % scans with EEG cap + montage = []; + montage.labelorg = { 'E29' 'E30' 'E31' 'E32'}'; + montage.labelnew = {'HEOG' 'ECG' 'VEOG'}'; + montage.tra = [1 -1 0 0; 0 0 1 0; 0 0 0 1]; + + case {'CP10168'} + % scans with EEG cap + montage = []; + montage.labelorg = {'HEOG' 'ECG' 'VEOG'}'; + montage.labelnew = {'HEOG' 'ECG' 'VEOG'}'; + montage.tra = eye(numel(montage.labelnew)); + + case {'CP10128', 'CP10129'} + % these are pilot subjects recorded in Glasgow + error('Subject %s is not supported', subjectid); + + + case {'CP10172', 'CP10173', 'CP10174', 'CP10188', 'CP10192', 'CP10195', 'CP10197', 'CP10198'}; + % These are the latest scans with finalized protocols. + % According to Abbas email the channels should be + % ECG: monopolar electrodes 'E1' and 'E2' + % VEOG: monopolar electrodes 'E3' and 'E4' + % HEOG: monopolar electrodes 'E5' and 'E6' + % EMG for hands and feet: bipolar electrodes 'E31', 'E32', 'E63', 'E64' + montage = []; + montage.labelorg = { 'ECG+' 'ECG-' 'VEOG+' 'VEOG-' 'HEOG+' 'HEOG-'}'; + montage.labelnew = {'ECG' 'VEOG' 'HEOG'}'; + montage.tra = [1 -1 0 0 0 0; 0 0 1 -1 0 0;0 0 0 0 1 -1]; + + + otherwise + % ASSUMING THAT ALL SUBJECTS THAT END UP HERE ARE FROM PHASE 2 + % WHERE THE ELECTRODE CONFIGURATION IS STANDARDIZED. + montage = []; + montage.labelorg = { 'ECG+' 'ECG-' 'VEOG+' 'VEOG-' 'HEOG+' 'HEOG-'}'; + montage.labelnew = {'ECG' 'VEOG' 'HEOG'}'; + montage.tra = [1 -1 0 0 0 0; 0 0 1 -1 0 0;0 0 0 0 1 -1]; + + end % switch + + + if ~isempty(regexp(scanid, 'Motort$', 'once')) + % VERY QUICK FIX for the latest 2 scans so that EOC ECG and EMG channels all exist in the data + montagenew = []; + montagenew.labelorg = {'ECG+' 'ECG-' 'VEOG+' 'VEOG-' 'HEOG+' 'HEOG-' 'EMG_LH' 'EMG_RH' 'EMG_LF' 'EMG_RF'}'; + montagenew.labelnew = {'ECG' 'VEOG' 'HEOG' 'EMG_LH' 'EMG_RH' 'EMG_LF' 'EMG_RF' }'; + montagenew.tra = zeros(7, 10); + montagenew.tra(1:3, 1:6) = montage.tra; + montagenew.tra(4:7, 7:10) = eye(4); + + montage = montagenew; + + elseif ~isempty(strfind(scanid, 'Motort')) && isempty(strfind(scanid, '_VG')) + % Motor task scans collected in SLU + Motorlabelorg = { 'E31' 'E32' 'E63' 'E64'}'; + Motorlabelnew = { 'EMG_LH' 'EMG_RH' 'EMG_LF' 'EMG_RF' }'; + + idxcol = []; + for i=1:length(Motorlabelorg) + sel = find(strcmp(Motorlabelorg(i), montage.labelorg)); + if ~isempty(sel) + idxcol = [idxcol sel]; + end + end + tra = montage.tra; + labelorg = montage.labelorg; + labelnew = montage.labelnew; + idxrow = find(any(tra(:, idxcol)')'); + tra(:, idxcol) = []; + tra(idxrow, :) = []; + labelorg(idxcol) = ''; + labelnew(idxrow) = ''; + + montage.labelorg = [labelorg; Motorlabelorg]; + montage.labelnew = [labelnew; Motorlabelnew]; + montage.tra = [tra, zeros(size(tra, 1), 4); zeros(4, size(tra, 2)), eye(4)]; + + % sort "montage.labelorg" + lab = char(montage.labelorg); + [sx, si] = sort(str2double(cellstr(lab(:, 2:end)))); + montage.labelorg = montage.labelorg(si); + montage.tra = montage.tra(:, si); + + elseif ~isempty(strfind(scanid, 'Motort')) && ~isempty(strfind(scanid, '_VG')) + error('Subject %s is not supported for MOTOR task', subjectid); + end + +end diff --git a/analysis_functions/hcp_extract_allfromrun.m b/analysis_functions/hcp_extract_allfromrun.m new file mode 100644 index 0000000..06c7cb9 --- /dev/null +++ b/analysis_functions/hcp_extract_allfromrun.m @@ -0,0 +1,348 @@ +function [cleanTrl] = hcp_extract_allfromrun(inCfg) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +datafile = inCfg.datafile; +icainfofile = inCfg.icainfofile; +trl = inCfg.trl; + +outputfile = inCfg.outputfile; +badsegmode = inCfg.badsegmode; % tmpCfg.badsegmode = 'remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. when each trial contins an entire block) +montage = inCfg.montage; % EEG and or EMG (for both one can use E*) +lineFreq = inCfg.lineFreq; + +badchanfile = inCfg.badchanfile; +badsegmfile = inCfg.badsegmfile; + +useTrlStd = 0; + +cfg = []; +cfg.trl = trl; +cfg.datafile = datafile; +cfgDefTr = ft_definetrial(cfg); +cfgDefTr.dataformat = '4d'; +cfgDefTr.headerformat = '4d'; +cfgDefTr.demean = 'no'; +dataRaw = ft_preprocessing(cfgDefTr); + +origFs = dataRaw.fsample; +% ===================== +% -- Define new sampling frequency to 500 Hz +if origFs>2000 + newFs = origFs/4; +elseif origFs>1000 + newFs = origFs/2; +else + newFs = origFs; +end +% ======================== + +elecChans = montage.labelnew'; +emgExpChans = {'EMG_LH'; 'EMG_RH';'EMG_LF'; 'EMG_RF'}; +[indElec, dummyInd] = match_str(elecChans, emgExpChans); + +if ~isempty(indElec) + tmpmontage = montage; + tmpmontage.tra = montage.tra(indElec, :); + hasELEC = 1; + emgChans = elecChans(indElec); + tmpmontage.labelnew = emgChans; + dataRawELECorg = ft_selectdata(dataRaw, 'channel', tmpmontage.labelorg); + dataRawELECnew = ft_apply_montage(dataRawELECorg, tmpmontage);clear dataRawELECorg; + dataRawELEC = ft_selectdata(dataRawELECnew, 'channel', emgChans);clear dataRawELECnew; + +else + hasELEC = 0; + dataRawELEC = []; +end + +% =========================================== + +hcp_read_matlab(icainfofile, 'comp_class'); +hcp_read_ascii(badchanfile);% badchannel +hcp_read_ascii(badsegmfile); % badsegment + +badChannels = badchannel.all; +badSegments = badsegment.all; + +% ================================================== +inData = dataRaw; clear dataRaw; + +% =============================================== +if ~isempty(badChannels) + badChannelsLabels = cellfun(@(x) ['-', x], badChannels, 'UniformOutput', false); +else + badChannelsLabels = []; +end + +% ------------------------------------------------------------------- +selChan = {'MEG', 'MEGREF'}; +if ~isempty(badChannelsLabels), + selChan = [selChan, badChannelsLabels]; +end +% ------------------------------------------------------------------- +dataClean0 = ft_selectdata(inData, 'channel', selChan); clear inData; + +% ======================================= +dataNEURO = ft_selectdata(dataClean0, 'channel', {'MEG', 'MEGREF'}); +clear dataClean0; +% ======================================= + +% ======================================= +% -- Reject bad segments +if strcmp(badsegmode, 'remfull') + cfg = []; + cfg.artfctdef.reject = 'complete'; + cfg.artfctdef.all.artifact = badSegments; + +elseif strcmp(badsegmode, 'repnan') + disp('Trial structure preserved. Bad segments replaced with nan'); + cfg = []; + cfg.artfctdef.reject = 'partial'; + cfg.artfctdef.all.artifact = badSegments; + + origDataNEURO = dataNEURO; % rmfield(dataNEURO, 'trial'); + if hasELEC + origDataELEC = dataRawELEC; % rmfield(dataRawELEC, 'trial'); + end + +end + +if strcmp(badsegmode, 'remfull') + + dataCleanNEURO1 = ft_rejectartifact(cfg, dataNEURO); + if hasELEC + dataCleanELEC1 = ft_rejectartifact(cfg, dataRawELEC); + end + +elseif strcmp(badsegmode, 'repnan') + dataCleanNEURO1 = rmfield(dataNEURO, {'trial', 'time', 'trialinfo', 'sampleinfo'}); + dataCleanNEURO1.trial = {}; + dataCleanNEURO1.time = {}; + dataCleanNEURO1.sampleinfo = []; + + for iTr = 1:length(dataNEURO.trial) + tmptrialdata = ft_selectdata(dataNEURO, 'rpt', iTr); + try + tmpcleantrialdata = ft_rejectartifact(cfg, tmptrialdata); + + + dataCleanNEURO1.trial = [dataCleanNEURO1.trial tmpcleantrialdata.trial]; + dataCleanNEURO1.time = [dataCleanNEURO1.time tmpcleantrialdata.time]; + dataCleanNEURO1.sampleinfo = [dataCleanNEURO1.sampleinfo ; tmpcleantrialdata.sampleinfo]; + catch + disp(['It seems that trial: ', num2str(iTr), ' falls entirely within a artifactual period']); + end + end + + if hasELEC + dataCleanELEC1 = rmfield(dataRawELEC, {'trial', 'time', 'trialinfo', 'sampleinfo'}); + dataCleanELEC1.trial = {}; + dataCleanELEC1.time = {}; + dataCleanELEC1.sampleinfo = []; + + for iTr = 1:length(dataRawELEC.trial) + tmptrialdata = ft_selectdata(dataRawELEC, 'rpt', iTr); + try + tmpcleantrialdata = ft_rejectartifact(cfg, tmptrialdata); + + dataCleanELEC1.trial = [dataCleanELEC1.trial tmpcleantrialdata.trial]; + dataCleanELEC1.time = [dataCleanELEC1.time tmpcleantrialdata.time]; + dataCleanELEC1.sampleinfo = [dataCleanELEC1.sampleinfo ; tmpcleantrialdata.sampleinfo]; + catch + disp(['It seems tha trial: ', num2str(iTr), ' falls entirely within a artifactual period']); + end + end + end % if hasELEC + +end +% ======================================= +cfg = []; +cfg.demean = 'yes'; +dataCleanNEURO1 = ft_preprocessing(cfg, dataCleanNEURO1); +if hasELEC + dataCleanELEC1 = ft_preprocessing(cfg, dataCleanELEC1); +end +if strcmp(badsegmode, 'repnan') + origDataNEURO = ft_preprocessing(cfg, origDataNEURO); + if hasELEC + origDataELEC = ft_preprocessing(cfg, origDataELEC); + end +end + +% =================================================== +% -- Denoise with Reference Sensors -- +cfg = []; +denDataNEURO = ft_denoise_pca(cfg, dataCleanNEURO1); +clear dataCleanNEURO1 ; + +% ======================================= +% -- Remove line noise -------------------- +for iLine = 1:length(lineFreq) + cfg = []; + cfg.detrend = 'no'; + cfg.bsfilter = 'yes'; + cfg.bsfreq = lineFreq(iLine)+[-1 1]; + denDataNEURO = ft_preprocessing(cfg, denDataNEURO); + if hasELEC + dataCleanELEC1 = ft_preprocessing(cfg, dataCleanELEC1); + end +end +dataCleanNEURO2 = denDataNEURO; +if hasELEC + dataCleanELEC2 = dataCleanELEC1; +end + +% ======================================= +denDataNEURO = dataCleanNEURO2; + +% ======================================= +% --- identify trials that have high std +% === THIS IS NOT USE BY DEFAULT ======= +% ---- Maybe it should be removed +Ntrials = length(dataCleanNEURO2.trial); + +if useTrlStd == 1 + stdRatio = 2; % Threshold of std of signle trial ./ std of all trials . + % See hcp_arti_usestd for details + [badTrials, trialsStdMat] = hcp_arti_usestd(dataCleanNEURO2, stdRatio); + % --- Remove the identified trials + cfg = []; + cfg.trials = 1:Ntrials; + cfg.trials(badTrials) = []; + cfg.feedback = 'no'; + denDataNEURO = ft_preprocessing(cfg, dataCleanNEURO2); clear dataCleanNEURO2 + dataCleanELEC2 = ft_preprocessing(cfg, dataCleanELEC2); +else + denDataNEURO = dataCleanNEURO2; clear dataCleanNEURO2 +end +% ====================================== + +% ====================================== +badicacomps = comp_class.class.ecg_eog_ic; +if ~isempty(badicacomps) + cfg = []; + cfg.component = badicacomps; + tmpcomp = comp_class; + tmpcomp.trial{1} = zeros(length(tmpcomp.label), 1); % dummy field + tmpcomp.time{1} = 0;% dummy field + dataNEURO = ft_rejectcomponent(cfg, tmpcomp, denDataNEURO); clear denDataNEURO; +else + dataNEURO = denDataNEURO; clear denDataNEURO; +end +% ========================================== +if strcmp(badsegmode, 'repnan') + [dataNEURO, trlNanFlag] = replaceorigwithnans(origDataNEURO, dataNEURO); clear origDataNEURO; + if hasELEC + dataCleanELEC2 = replaceorigwithnans(origDataELEC, dataCleanELEC2); clear origDataELEC; + end +end +% ======================================= +% -- Append ELEC and MEG/EEG + +if hasELEC + data = ft_appenddata([], dataNEURO, dataCleanELEC2); + clear dataCleanELEC2; +else + data = dataNEURO; +end; +% ======================================= +if strcmp(badsegmode, 'repnan') + addTrlColmn = trlNanFlag; +elseif strcmp(badsegmode, 'remfull') + addTrlColmn = zeros(length(data.trial), 1); +end +data.trialinfo = [data.trialinfo addTrlColmn]; +% ======================================= +data.grad = dataNEURO.grad; +clear dataNEURO; + + +% ===================== +% -- Resample to 500 Hz +data.fsample = origFs; +rscfg = []; +rscfg.detrend = 'no'; +rscfg.resamplefs = newFs; +data = ft_resampledata(rscfg, data); % clear data; + +% ========================= +cleanTrl = data.trialinfo; +varinfo = whos('data'); +if (varinfo.bytes/1000000000)>2, + hcp_write_matlab(outputfile, 'data', '-v7.3'); +else + hcp_write_matlab(outputfile, 'data'); +end +clear data; + +end % function + +% ====================================================== +% ====================================================== +% ====================================================== +% ====================================================== + +function[newdata, trlNanFlag] = replaceorigwithnans(origdata, cleandata) +% The original data is the one before the partial artifact rejection +% The clean data is after the partial artifact rejection +% The function outputs a newdata with the same trial structure as the +% origdata but with the cleandata data in the clean periods and nans +% in the noisyperiosds; It also outputs an array trlNanFlag with length +% equal to the trials in the newdata and flags with 1 the trials that have +% nans and 0's otherwise. +% The original data is the one before the partial artifact rejection +% The clean data is after the partial artifact rejection + +newdata = origdata; +origSampinfo = origdata.sampleinfo; +cleanSampinfo = cleandata.sampleinfo; + +NtrlOrig = size(origSampinfo, 1); +NtrlClean = size(cleanSampinfo, 1); + +Nchans = length(cleandata.label); + +trlNanFlag = zeros(NtrlOrig, 1); +for iOrig = 1:NtrlOrig, + % iOrig + origStart = origSampinfo(iOrig, 1); + origEnd = origSampinfo(iOrig, 2); + tmptrldata = nan(Nchans, origEnd-origStart+1); + + for iClean = 1:NtrlClean + cleanStart = cleanSampinfo(iClean, 1); + cleanEnd = cleanSampinfo(iClean, 2); + if (cleanStart == origStart) && (cleanEnd == origEnd) + tmptrldata = cleandata.trial{iClean}; + else + [comSamps, indxOrig, indxClean] = intersect([origStart:origEnd], [cleanStart:cleanEnd]); + if ~isempty(comSamps) + tmptrldata(:, indxOrig) = cleandata.trial{iClean}(:, indxClean); + end + end + + end + if any(isnan(tmptrldata(1, :))) + trlNanFlag(iOrig) = 1; + end + newdata.trial{iOrig} = tmptrldata; +end +newdata.label = cleandata.label; + +end % function diff --git a/analysis_functions/hcp_fclose.m b/analysis_functions/hcp_fclose.m new file mode 100644 index 0000000..39c3c8b --- /dev/null +++ b/analysis_functions/hcp_fclose.m @@ -0,0 +1,31 @@ +function hcp_fclose(varargin) + +% HCP_FCLOSE works just like FOPEN/FPRINTF/FCLOSE but also writes an XML +% provenance file as soon as the text file is closed. +% +% Example +% fid = hcp_fopen(filename, 'w') +% hcp_fprintf(fid, 'the number is %d\n', 42); +% hcp_fclose(fid) +% +% See also HCP_FOPEN, HCP_FPRINTF, HCP_FCLOSE, HCP_WRITE_PROVENANCE + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% use the general function, it keeps track of all files +hcp_fprintf('close', varargin{:}); diff --git a/analysis_functions/hcp_fitspectrum.m b/analysis_functions/hcp_fitspectrum.m new file mode 100644 index 0000000..4c9c495 --- /dev/null +++ b/analysis_functions/hcp_fitspectrum.m @@ -0,0 +1,73 @@ +function [fitx, goodness] = hcp_fitspectrum(x, y) + +% HCP_FITSPECRUM fits a "one-over-f plus constant" model to a power spectrum +% +% The model that is fitted is +% y = a/x + b +% with positive a and b. +% +% Example: +% x = (1:100)'; +% y = 2./x + 3 + 0.1*rand(size(x)); +% [fitx, goodness] = hcp_fitspectrum(x, y) +% +% This replaces the following use of the fit() function from +% the Mathworks curve fitting toolbox +% g = fittype('abs(a)*1/x + abs(b)') +% [fitx, goodness] = fit(x, y, g) +% +% Note that this implementation does not guarantee that a +% and b are positive. + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +x = x(:); +y = y(:); +n = numel(x); + +if false + % convert the model into a linear one, i.e. z = a + x*b + z = y.*x; + beta = [ones(n,1) x] \ z; +else + beta = [1./x ones(n,1)] \ y; +end + +a = beta(1); +b = beta(2); + +if a<0 + warning('the fitted parameter a is not positive'); +end +if b<0 + warning('the fitted parameter b is not positive'); +end + +% compute the estimation +est = a./x + b; + +c = corrcoef(y, est); + +fitx.a = a; +fitx.b = b; +fitx.est = est; + +goodness.rsquare = c(1,2).^2; +goodness.sse = sum((y-est).^2); +goodness.dfe = n-2; +goodness.rmse = sqrt(goodness.sse/goodness.dfe); diff --git a/analysis_functions/hcp_fopen.m b/analysis_functions/hcp_fopen.m new file mode 100644 index 0000000..88a9b1b --- /dev/null +++ b/analysis_functions/hcp_fopen.m @@ -0,0 +1,31 @@ +function fid = hcp_fopen(varargin) + +% HCP_FOPEN works just like FOPEN/FPRINTF/FCLOSE but also writes an XML +% provenance file as soon as the text file is closed. +% +% Example +% fid = hcp_fopen(filename, 'w') +% hcp_fprintf(fid, 'the number is %d\n', 42); +% hcp_fclose(fid) +% +% See also HCP_FOPEN, HCP_FPRINTF, HCP_FCLOSE, HCP_WRITE_PROVENANCE + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% use the general function, it keeps track of all files +fid = hcp_fprintf('open', varargin{:}); diff --git a/analysis_functions/hcp_fprintf.m b/analysis_functions/hcp_fprintf.m new file mode 100644 index 0000000..4bceafc --- /dev/null +++ b/analysis_functions/hcp_fprintf.m @@ -0,0 +1,48 @@ +function fid = hcp_fprintf(fid, varargin) + +% HCP_FPRINTF works just like FOPEN/FPRINTF/FCLOSE but also writes an XML +% provenance file as soon as the text file is closed. +% +% Example +% fid = hcp_fopen(filename, 'w') +% hcp_fprintf(fid, 'the number is %d\n', 42); +% hcp_fclose(fid) +% +% See also HCP_FOPEN, HCP_FPRINTF, HCP_FCLOSE, HCP_WRITE_PROVENANCE + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +persistent remember +if isempty(remember) + remember = {}; +end + +if ischar(fid) && strcmp(fid, 'open') + fid = fopen(varargin{:}); + remember{fid} = varargin; % remember the file + +elseif ischar(fid) && strcmp(fid, 'close') + fid = varargin{1}; + hcp_write_provenance(remember{fid}{1}); % write the provenance + remember(fid) = []; % forget about the file + fclose(fid); + +else + fprintf(fid, varargin{:}); + +end diff --git a/analysis_functions/hcp_getopt.m b/analysis_functions/hcp_getopt.m new file mode 100644 index 0000000..4c0ed3a --- /dev/null +++ b/analysis_functions/hcp_getopt.m @@ -0,0 +1,64 @@ +function [opts, args] = hcp_getopt(varargin) + +% HCP_GETOPT parses the command line to the megconnectome executable +% application, separating the options that start with -- from the file +% names of the scripts to be executed. +% +% Use as +% megconnectome.exe --option1 arg1 --option2 arg2 scriptA.m scriptB.m +% splits the command line arguments into a cell array with key-value pairs +% and a cell array with the filenames. +% +% In this example the hcp_getopt function returns +% opts = {'option1', arg1, 'option2', arg2}; +% args = {'scriptA.m', 'scriptB.m'} +% +% See also FT_GETOPT + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +opts = {}; +args = {}; + +i = 1; +while (i<=nargin) + if ischar(varargin{i}) && strncmp('--', varargin{i}, 2) + % assume that it is a --option + opts{end+1} = varargin{i}(3:end); % remove the -- from the option + try + opts{end+1} = varargin{i+1}; % the value + catch + opts{end+1} = []; % there are not enough values + end + i = i + 2; + elseif ischar(varargin{i}) && strncmp('-', varargin{i}, 1) + % assume that it is a -option + opts{end+1} = varargin{i}(2:end); % remove the - from the option + try + opts{end+1} = varargin{i+1}; % the value + catch + opts{end+1} = []; % there are not enough values + end + i = i + 2; + else + % assume that it is a file name + args{end+1} = varargin{i}; + i = i + 1; + end +end + diff --git a/analysis_functions/hcp_ica_plotreport.m b/analysis_functions/hcp_ica_plotreport.m new file mode 100644 index 0000000..0955b28 --- /dev/null +++ b/analysis_functions/hcp_ica_plotreport.m @@ -0,0 +1,194 @@ +function hcp_ica_plotreport(comp,options_ICA,datain) + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +resultprefix =ft_getopt(options_ICA, 'resultprefix'); +subject = ft_getopt(options_ICA, 'subject'); +bandpass = ft_getopt(options_ICA, 'bandpass'); +bandstop = ft_getopt(options_ICA, 'bandstop'); +tsize = ft_getopt(options_ICA, 'textsize',22) +% grad = ft_getopt(options_ICA, 'grad'); +modality = ft_getopt(options_ICA, 'modality', 'MEG'); % GIORGOS +grad=ft_getopt(options_ICA, 'grad'); + +if strcmp(modality,'MEG') + layout='4D248.mat'; +elseif strcmp(modality,'EEG') + layout='EEG1010.lay'; +end + +if(~isfield(comp,'trial')) + for i=1:size(datain.trial,2) + comp.trial{i} = comp.unmixing*datain.trial{i}; + end +end + +comp.time={cell2mat(datain.time)}; + + +if(~isfield(comp,'pow_data')) + doplot='no'; % Summury Results cn be plotted also at this level without calling hcp_plotICA + options = {'doplot',doplot, 'grad', grad,'plottype','components'}; + + [comp_freq]=hcp_ICA_freq(comp, options, datain); + comp.pow_data=comp_freq.pow_data; + comp.freq_comp=comp_freq.freq_comp; +end +n_IC=size(comp.unmixing,1); + +frange=bandpass; %to be decided if in compo or other + +cfgin =[]; +cfgin.grad=grad; +cfgin.zlim='maxabs'; +cfgin.component=[]; +cfgin.parameter = 'topo'; +cfgin.comment = 'no'; +cfgin.colorbar = 'yes'; + + +mspec = sqrt(comp.freq_comp.powspctrm); +F = comp.freq_comp.freq; +pow_data=comp.pow_data; + +pIC = cell2mat(pow_data.trial).^2; +time_pIC=cell2mat(pow_data.time); + + +leg={'OneOverF (>0.5) ' ; 'Specflat (<2.0) ' ; 'Kurtosis (>15) ' ; 'elecSig (>0.1) '; 'elecPow (>0.25) '; 'elecSpe (>0.95) '}; +thrs=[0.5;2.0;15;0.1;0.25;0.95]; +class=comp.class; +% size_s=get(0,'ScreenSize'); +for ix=1:n_IC + str(1,:)={num2str(abs(comp.class.spectrum_one_over_f(1,ix)),'%5.3f')}; + str(2,:)={num2str(abs(comp.class.spectrum_flat(1,ix)),'%5.1f')}; + str(3,:)={num2str(abs(comp.class.time_kurtosis(1,ix)),'%5.1f')}; + str(4,:)={num2str(abs(comp.class.elc_signal_correlation(1,ix)),'%5.3f')}; + str(5,:)={num2str(abs(comp.class.elc_power_correlation(1,ix)),'%5.3f')}; + str(6,:)={num2str(abs(comp.class.elc_spectrum_correlation(1,ix)),'%5.3f')}; + strcolor=['k' ;'k'; 'k'; 'k'; 'k'; 'k']; + for i=[1 3 4 5 6] + if(str2num(str{i})>thrs(i)) strcolor(i)='b';end + end + if(str2num(str{2})1 & F. + +% the MATLAB exists function will look for the file anywhere on the path +% which returns the actual full file with path + +filewithpath = hcp_which(filename); + +if isempty(filewithpath) + error('file "%s" does not exist', filename); +else + filename = filewithpath; + clear filewithpath +end + +% status will be 0 once the md5sum is known +status = -1; + +if status~=0 + [status, str] = system(sprintf('md5sum %s', filename)); + % this looks like + % ee338cf30b7e10f249b61dade77339d1 filename + hash = strtok(str); +end + +if status~=0 + [status, str] = system(sprintf('md5 %s', filename)); + % this looks like + % MD5 (filename) = ee338cf30b7e10f249b61dade77339d1 + [tok, rem] = strtok(str, '='); + hash = strtrim(rem(2:end)); +end + +if status~=0 + warning('failed to compute MD5 hash for file "%s', filename); + hash = 'unknown'; +end diff --git a/analysis_functions/hcp_pathdef.m b/analysis_functions/hcp_pathdef.m new file mode 100644 index 0000000..a6ba007 --- /dev/null +++ b/analysis_functions/hcp_pathdef.m @@ -0,0 +1,68 @@ +function [pipelinedatadir, rawdatadir] = hcp_pathdef + +% HCP_PATHDEF returns the directories where the raw and processed data +% can be found on this computer, together with a list of all raw datasets. +% +% Use as +% [pipelinedatadir, rawdatadir] = hcp_pathdef +% which returns +% pipelinedatadir = string +% rawdatadir = string +% dataset = cell-array with strings +% +% See also HCP_DB_QUERY, HCP_DB_GETFILENAME + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% this is where the pipeline output is written and where a pipeline can find the output from the preceding steps +pipelinedatadir = hcp_select_datadir({ + '/home/mrphys/roboos/data/hcp/processedRO' % this should go before jansch + '/home/language/jansch/public/hcp/processedJM' + '/data/jansch/HCP/database/processedJM' + '/data/roboos/pipeline' + '/scratch/r.oostenveld/pipeline' + '/mnt/hps/slurm/michalareasg/pipeline' + % 'N:\pipeline_testing' + % 'N:\PhaseII_testing' + % 'N:\PhaseII_testing_x-nat' + % 'N:\PhaseII_testing_newfit' + % 'N:\PhaseII_testing_newfit\bump_problem_analysis' + 'N:\PhaseII_testing_newfit\new_win_filter' + 'W:\PhaseII_testing_newfit' %%%point to W:\PhaseII_testing\laura dir for elaborated data + % 'N:\PhaseII_testing_MEG_EEG' + + pwd + }); + +% specify where the raw data can be found +rawdatadir = hcp_select_datadir({ + '/data/HCP/webdav' + '/data/jansch/HCP/database' + '/data/roboos/webdav' + '/home/mrphys/roboos/data/hcp/webdav' % this should go before jansch + '/home/language/jansch/public/hcp/webdav' + '/mnt/hps/home/michalareasg/data/hcp/database' + '/scratch/r.oostenveld/webdav' + 'D:\webdav' + % 'E:\HCP\webdav' + 'I:\webdav' + % 'N:\Phase1MEG_fra\webdav' + 'N:\webdav' + 'W:\webdav\' + }); + diff --git a/analysis_functions/hcp_printstruct.m b/analysis_functions/hcp_printstruct.m new file mode 100644 index 0000000..3bd03dd --- /dev/null +++ b/analysis_functions/hcp_printstruct.m @@ -0,0 +1,28 @@ +function varargout = hcp_printstruct(varargin) + +% HCP_PRINTSTRUCT is a robust ewrapper around the corresponding FieldTrip +% function. + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +try + varargout{:} = printstruct(varargin{:}); +catch + warning(lasterr) + varargout{1} = sprintf('%s could not be printed to the screen due to the error above\n', varargin{1}); +end diff --git a/analysis_functions/hcp_provenance.m b/analysis_functions/hcp_provenance.m new file mode 100644 index 0000000..08205fa --- /dev/null +++ b/analysis_functions/hcp_provenance.m @@ -0,0 +1,63 @@ +function prov = hcp_provenance + +% HCP_PROVENANCE returns a structure with provenance information + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +prov.version.matlab = ver('MATLAB'); + +prov.version.megconnectome = ver('megconnectome'); +if isempty(prov.version.megconnectome) + prov.version.megconnectome = 'unknown'; % otherwise struct2xml fails +end + +prov.version.fieldtrip = ver('fieldtrip'); +if isempty(prov.version.fieldtrip) + prov.version.fieldtrip = 'unknown'; % otherwise struct2xml fails +end + +if isdeployed + prov.compiled = 'true'; +else + prov.compiled = 'false'; +end + +% it is not desired to keep the date and time of execution of the analysis pipeline +% see https://github.com/Washington-University/megconnectome/issues/126 +% prov.time = datestr(now); + +prov.username = getusername; +prov.hostname = gethostname; +prov.architecture = computer('arch'); +if isdeployed + % the buildtimestamp function is created on the fly by the compile script + prov.buildtimestamp = buildtimestamp; +else + prov.buildtimestamp = []; +end +prov.pwd = pwd; +prov.matlabstack = printstack(dbstack); % using private helper function +prov.Attributes.xmlns_colon_xsi = 'http://www.w3.org/2001XMLSchema-instance'; +prov.Attributes.xsi_colon_noNamespaceSchemaLocation = 'megconnectome.xsd'; + +function str = printstack(s) +str = sprintf('\n'); +% skip the first, it is always hcp_provenance and not interesting +for i=2:length(s) + str = [str sprintf('In %s at %d\n', s(i).name, s(i).line)]; +end diff --git a/analysis_functions/hcp_pseudoeocgfromtopo.m b/analysis_functions/hcp_pseudoeocgfromtopo.m new file mode 100644 index 0000000..c6b60a8 --- /dev/null +++ b/analysis_functions/hcp_pseudoeocgfromtopo.m @@ -0,0 +1,243 @@ +function [pseudodataECG,pseudodataEOG] = hcp_pseudoeocgfromtopo(inputCfg, iteration, datain) + +% This function is used by hcp_icaclass script. It inputs ICA components +% and computes vector angle with a template topology for Eye and Heart artifacts. +% Based on +% Yandong Li et al 2006 Physiol. Meas. 27 425 doi:10.1088/0967-3334/27/4/008 +% +% INPUTS +% inputCfg.templateICAFile: This a mat file containing topology +% templates for EOG and ECG comopnents. (Currently in sandbox) +% inputCfg.ouputfile : Filename where to print the EOG and ECG +% recognized IC components. At the end of the name is appended ECG and +% VEOG respectively +% iteration: Structure containing multiple comp structures from +% multiple ICA decompositions. Computed in hcp_icaclass +% datain: The data that was used for the above ICA decompositions. +% Computed in hcp_icaclass +% OUTPUTS +% pseudodataECG: data structure containing one virtual channel 'ECG' +% pseudodataEOG: data structure containing one virtual channel 'VEOG' + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +%===================================== +Nrecurse=length(iteration); +templateICAFile=inputCfg.templateICAFile; +outputDataFile=inputCfg.outputfile; + +hcp_read_matlab(templateICAFile,'compeyes','compheart'); + +%---------------------------------------------------- +%===================================== + +tmpChanInd=match_str(compeyes.topolabel,datain.label); +eyeUnmix=compeyes.unmixing(tmpChanInd); +eyeTopo=compeyes.topo(tmpChanInd); +tmpChanInd=match_str(compheart.topolabel,datain.label); +heartUnmix=compheart.unmixing(tmpChanInd); +heartTopo=compheart.topo(tmpChanInd); +Ntrials=length(datain.trial); +%===================================== + +%% +totMinAngDistEye=[]; +totMinAngDistHeart=[]; +totMinAngIndxDistEye=[]; +totMinAngIndxDistHeart=[]; +for iRep=1:Nrecurse, + + + comp=iteration(iRep).comp; + + + NicaComputed=size(comp.topo,2); + + angDistEye=[]; + angDistHeart=[]; + for iComp=1:NicaComputed, + angDistEye(iComp)=acos(abs(comp.topo(:,iComp)'*eyeTopo)./(norm(comp.topo(:,iComp))*norm(eyeTopo))); + angDistHeart(iComp)=acos(abs(comp.topo(:,iComp)'*heartTopo)./(norm(comp.topo(:,iComp))*norm(heartTopo))); + end + + [tmpMinEyeAng,indMinEye]=min(angDistEye); + [tmpMinHeartAng,indMinHeart]=min(angDistHeart); + + totMinAngDistEye=tmpMinEyeAng; + totMinAngDistHeart=tmpMinHeartAng; + totMinAngIndxDistEye=indMinEye; + totMinAngIndxDistHeart=indMinHeart; + + %tmpMinEyeAng + %tmpMinHeartAng + figure; + plot(angDistEye); + hold on; + plot(angDistHeart,'r'); + pause(5); + close; + +end + + +[totalMinEye,indRecMinEye]=min(totMinAngDistEye); +[totalMinHeart,indRecMinHeart]=min(totMinAngDistHeart); +recIndxEOG=totMinAngIndxDistEye(indRecMinEye); +recIndxECG=totMinAngIndxDistHeart(indRecMinHeart); + + + + + +%% +%{ +threshAngle=(pi/4); +[indxEOG]=find(angDistEye<=threshAngle); +[indxECG]=find(angDistHeart<=threshAngle); +%} +%----------- +tmpcomp=iteration(indRecMinHeart).comp; +compECG=tmpcomp; +for iTrial=1:Ntrials, + compECG.trial{iTrial}=tmpcomp.unmixing(recIndxECG,:)*datain.trial{iTrial}; +end +compECG.time=datain.time; +compECG.label=compECG.label(recIndxECG); +compECG.topo=tmpcomp.topo(:,recIndxECG); +compECG.unmixing=tmpcomp.unmixing(recIndxECG,:); +pseudodataECG=rmfield(compECG,{'unmixing','grad','sampleinfo','topo','topolabel','order'}); +pseudodataECG.sampleinfo=datain.sampleinfo; +pseudodataECG.grad=datain.grad; +pseudodataECG.label={'ECG'}; +%----------- +tmpcomp=iteration(indRecMinEye).comp; +compEOG=tmpcomp; +for iTrial=1:Ntrials, + compEOG.trial{iTrial}=tmpcomp.unmixing(recIndxEOG,:)*datain.trial{iTrial}; +end +compEOG.time=datain.time; +compEOG.label=compEOG.label(recIndxEOG); +compEOG.topo=tmpcomp.topo(:,recIndxEOG); +compEOG.unmixing=tmpcomp.unmixing(recIndxEOG,:); +pseudodataEOG=rmfield(compEOG,{'unmixing','grad','sampleinfo','topo','topolabel','order'}); +pseudodataEOG.sampleinfo=datain.sampleinfo; +pseudodataEOG.grad=datain.grad; +pseudodataEOG.label={'VEOG'}; +%===================================================== +%============= PLOT AND SAVE ================= + + + + +%======================================================== +%---- PLOT ECG + +Naddtrials=Ntrials; +Nsamps=size(compECG.trial{1},2); +Ncomps=length(compECG.label); +tmpConcData=([compECG.trial{1:Naddtrials}]); + +cfg = []; +cfg.method = 'mtmfft'; +cfg.taper = 'hanning'; +cfg.foi = [0:0.5:40]; +freq = ft_freqanalysis(cfg, compECG); + +cfg=[]; +cfg.layout='4D248.mat'; +cfg.comment=' '; +f1=figure('position',[40 213 870 421]); + +ax2=axes('position',[0.1 0.45 0.25 0.5]); +cfg.component=1; +ft_topoplotIC(cfg,compECG); +title(['ECG comp']); +ax3=axes('position',[0.4 0.5 0.18 0.36]); +cfg.component=1; +compheart.time=compECG.time; +compheart.trial=compECG.trial; +ft_topoplotIC(cfg,compheart); +title('template'); +ax4=axes('position',[0.65 0.45 0.25 0.5]); +plot(freq.freq,freq.powspctrm,'linewidth',3); +title('Spectrum'); +xlabel('freq(Hz)'); +tmpTime=(1./compECG.fsample)*[1:length(tmpConcData)]; +ax1=axes('position',[0.1 0.1 0.8 0.3]); +plot(tmpTime,tmpConcData); +axis tight; +title('sample interval'); +xlabel('time(sec)'); + +figName=[outputDataFile,'ECG.png']; +hcp_write_figure(figName, f1); + + +%========================================================= +%========================================================= + +%======================================================== +%---- PLOT EOG + + + + + +Naddtrials=Ntrials; +Nsamps=size(compEOG.trial{1},2); +Ncomps=length(compEOG.label); +tmpConcData=([compEOG.trial{1:Naddtrials}]); + +cfg = []; +cfg.method = 'mtmfft'; +cfg.taper = 'hanning'; +cfg.foi = [0:0.5:40]; +freq = ft_freqanalysis(cfg, compEOG); + +cfg=[]; +cfg.layout='4D248.mat'; +cfg.comment=' '; +f1=figure('position',[40 213 870 421]); + +ax2=axes('position',[0.1 0.45 0.25 0.5]); +cfg.component=1; +ft_topoplotIC(cfg,compEOG); +title(['EOG comp.']); +ax3=axes('position',[0.4 0.5 0.18 0.36]); +cfg.component=1; +compeyes.time=compEOG.time; +compeyes.trial=compEOG.trial; +ft_topoplotIC(cfg,compeyes); +title('template'); +ax4=axes('position',[0.65 0.45 0.25 0.5]); +plot(freq.freq,freq.powspctrm,'linewidth',3); +title('Spectrum'); +xlabel('freq(Hz)'); +tmpTime=(1./compEOG.fsample)*[1:length(tmpConcData)]; +ax1=axes('position',[0.1 0.1 0.8 0.3]); +plot(tmpTime,tmpConcData); +axis tight; +title('sample interval'); +xlabel('time(sec)'); + +figName=[outputDataFile,'VEOG.png']; +hcp_write_figure(figName, f1); + +%========================================================= +%========================================================= + diff --git a/analysis_functions/hcp_qc_neighcorrel.m b/analysis_functions/hcp_qc_neighcorrel.m new file mode 100644 index 0000000..930b5ee --- /dev/null +++ b/analysis_functions/hcp_qc_neighcorrel.m @@ -0,0 +1,300 @@ +function [badchannel_neighcorr, dataclean] = hcp_qc_neighcorrel (dataraw,options_correl) + +% This function loads the entire raw MEG data and check for channels that have low +% correlation with their neighbours. The average correlation with neighbours +% within a distance of 0.037m is between 0.6 and 0.8. So the user here can set a +% threshold for identifying such "bad" sensors. The reason for using this approach +% is that "bad" sensor is one that has inherent noise uncorrelated with other +% sensors. Typically the same sensors should show the lowest correlation with +% neighbours for different datasets. In order to avoid cases where the average +% correlation of the entire sensor array is low (when subject's head is away from +% the sensor) an additional threshold is used. This is the mean neighbour +% correlation from all channels minus 3 standard deviations of the neighbour +% correlation of all channels. So in order a bad channel to be selected , its +% neighbour correlation should also lower than this additional threshold. +% +% INPUTS: +% dataraw : This is the raw data structure. It should contain the +% entire dataset in one continous trial. +% +% options_correl: This is a row cell which key-value pairs in which the +% parameters for the neighbour correlation are set. These paramters +% are: +% +% resultprefix: This is a char containing the subject and +% experiment ID and which is used to name the plots created +% by this function +% +% corrthreshold: The threshold on correlation with neighbours for each +% sensor, below which channels are +% identified as bad. An +% emprirically sensible value is 0.4. +% +% OUPUTS: +% badchannel_neighcorr: : These are the labels of bad sensors sorted according +% to the corelation ascending +% dataclean11 : This is a data structure , identical to the input dataset dataraw but +% with the bad channels identified from the neighbour correlation metric discarded + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +resultprefix = ft_getopt(options_correl, 'resultprefix'); +corrthreshold = ft_getopt(options_correl, 'corrthreshold'); % skipped intervals previously identified + +Fsample=dataraw.fsample; + + +if length(dataraw.trial)==1, + disp(['input dataset has a single trial - spliting in pseudo trials']); + + nSamples=size(dataraw.trial{1},2); + defTrDur=1; %sec + defTrSamps=floor(defTrDur.*Fsample); + Ntrials=floor(nSamples./defTrSamps); + trlStart=defTrSamps.*((0:Ntrials-1)')+1; + trlEnd=defTrSamps.*((1:Ntrials)'); + trlDur=repmat(0,Ntrials,1); + trl=[trlStart trlEnd trlDur]; + cfg = []; + cfg.trl=trl; + cfg.headerformat='4d'; + cfg.dataformat='4d'; + dataclean1=ft_redefinetrial(cfg,dataraw); + datachannels=ft_channelselection('MEG',dataclean1.label); + + +else + error(['input dataset should have the entire data set in a single trial']); + return; +end + + +cfg = []; +cfg.detrend = 'yes'; +cfg.feedback = 'none'; +cfg.headerformat='4d'; +cfg.dataformat='4d'; +dataclean1=ft_preprocessing(cfg,dataclean1); + + + + +Nchannels=length(dataclean1.label); +%================================================== +%--COMPUTE CORRELATION OF ALL CHANNELS WITH ALL CHANNELS +trialsCorr = cellfun(@(x) corrcoef(x'), dataclean1.trial, 'UniformOutput', false); +for iTr=1:Ntrials, + trialsCorr{iTr}(isnan(trialsCorr{iTr}))=0; +end +%==================================== +%-- COMPUTE NEIGHBOURS + +cfg = []; +cfg.method='distance'; +cfg.grad = dataraw.grad; +cfg.neighbourdist = 0.037; %second order neighbours +layout='4D248.mat'; + +neighLabels = ft_prepare_neighbours(cfg); +%-------------- +allCorrelLabels=dataclean1.label; +tmpNeighLabels=struct2cell(neighLabels); +neighIndices=cell(Nchannels,1); +for iChan = 1:Nchannels + baseChan=dataclean1.label{iChan}; + [indx1,indx2] = match_str(tmpNeighLabels(1,1,:), baseChan); + tmpNeighChannels=tmpNeighLabels{2,1,indx1}; + [indx1,indx2] = match_str(dataclean1.label,tmpNeighChannels); + tmpNeighIndx=indx1'; + neighIndices{iChan}=tmpNeighIndx; +end +%============================================= +%-- COMPUTE AVERAGE CORRELATION WITH NEIGHBOURS +[trialsCorrAv]=cellfun(@compute_avercorr, trialsCorr,repmat({neighIndices},1,Ntrials), 'UniformOutput', false); +trialsCorrAv=cell2mat(trialsCorrAv); +avgCorrPerChan=(mean(trialsCorrAv,2)); +%============================================= +%----- USE THRESHOLD TO SELECT BAD CHANNELS +tmpThres=mean(avgCorrPerChan(:))-3*std(avgCorrPerChan); +[indBad]=find((avgCorrPerChan. + +resultprefix = ft_getopt(options_std, 'resultprefix'); +stdratiothreshold = ft_getopt(options_std, 'stdratiothreshold'); % skipped intervals previously identified + + +%======================================= +%-- Remove line noise -------------------- +tok = tokenize(resultprefix, '_'); +subjID=tok{1}; +if strcmp(subjID,'CP10128')|strcmp(subjID,'CP10129') + linefreq=[50 100]; +else + linefreq=[60 120]; +end +%{ +cfg=[]; +cfg.detrend = 'no'; +cfg.dftfilter = 'yes'; +cfg.dftfreq = linefreq; +datain = ft_preprocessing(cfg,datain); +%} + +cfg=[]; +cfg.bsfilter = 'yes'; +cfg.bsfreq = linefreq(1)+[-1 1]; +datain = ft_preprocessing(cfg,datain); +cfg.bsfreq = linefreq(2)+[-1 1]; +datain = ft_preprocessing(cfg,datain); + +%======================================= + + +% ======================================= +%--- Select MEG data +%channelData=ft_selectdata(datain,'channel',{'MEG','-AF*'}); +channelData=datain; +totalData=[channelData.trial{:}]; +stdTotal=std(totalData,[],2); +Nchannels=size(totalData,1); +%==================================== + + +% ======================================= +%--- COMPUTE NEIGHBOURS ------------ +cfg = []; +cfg.method='distance'; +cfg.grad = channelData.grad; + +cfg.neighbourdist = 0.037; %second order neighbours +layout='4D248.mat'; + +neighLabels = ft_prepare_neighbours(cfg); +%-------------------------------------- +tmpNeighLabels=struct2cell(neighLabels); +neighIndices=cell(Nchannels,1); +for iChan = 1:Nchannels + baseChan=channelData.label{iChan}; + [indx1,indx2] = match_str(tmpNeighLabels(1,1,:), baseChan); + tmpNeighChannels=tmpNeighLabels{2,1,indx1}; + [indx1,indx2] = match_str(channelData.label,tmpNeighChannels); + tmpNeighIndx=indx1'; + neighIndices{iChan}=tmpNeighIndx; +end +%==================================== + +%==================================== +%------- COMPUTE STD RATIO +stdRatio=zeros(1,Nchannels); +for iChan=1:Nchannels, + baseStd=stdTotal(iChan); + tmpNeighStd=stdTotal(neighIndices{iChan}); + meanNeighStd=mean(tmpNeighStd); + stdRatio(iChan)=(baseStd-meanNeighStd)./meanNeighStd; +end + +meanstdratio=mean(stdRatio); +stdstdratio=std(stdRatio); + +%indBad=find(abs(stdRatio)>stdratiothreshold); +indBad=find((abs(stdRatio)>stdratiothreshold)&(abs(stdRatio-meanstdratio)>= 3*stdstdratio)); + + +if ~isempty(indBad) + badchannel_neighstd=channelData.label(indBad)'; +else + badchannel_neighstd={}; +end + +% badChan=[]; +% badChan.stdratiothreshold=stdratiothreshold; +% if ~isempty(indBad) +% badChan.label=channelData.label(indBad); +% +% else +% badChan.label=[]; +% end +% badChan.stdRatio=stdRatio; + +%========================================== + +%========================================== +%----- SAVE AND PLOT +%----- simple plot +f1=figure; +plot(stdRatio,'.') +hold on +plot(indBad,stdRatio(indBad),'.r','markersize',20); +legend('all','bad'); +plot(stdRatio); +for iB=1:length(indBad), + text(indBad(iB),stdRatio(indBad(iB)),channelData.label(indBad(iB))) +end +title('Std Difference Ratio with neighbours - red is bad'); +xlabel('# Channel') +%--------------------- +%-------------------------------- +%----- TOPOPLOT of std ratio with bad sensors marked +[indx1,indx2]=match_str(channelData.grad.label, channelData.label); +sens3Dpos=channelData.grad.chanpos(indx1,:); +cfg=[]; +cfg.layout=layout; +sensLay=ft_prepare_layout(cfg); +tmpLay=sensLay; + + +% r=0.1; +% ang=[0:0.01:2*pi]'; +% xadd=r*cos(ang); +% yadd=r*sin(ang); +% x_left= -0.384100425519062+xadd; +% y_left= 0.345302231087568+yadd; +% x_right= 0.389749219378296+xadd; +% y_right= 0.345302231087568+yadd; +% tmpLay.mask{2}=[x_left y_left]; +% tmpLay.mask{3}=[x_right y_right]; + + +[indx1topo,indx2topo]=match_str( channelData.label,tmpLay.label); +tmpLay.pos=tmpLay.pos(indx2topo,:); +tmpLay.width=tmpLay.width(indx2topo); +tmpLay.height=tmpLay.height(indx2topo); +tmpLay.label=tmpLay.label(indx2topo); +%--------------------------- +chanX=tmpLay.pos(:,1); +chanY=tmpLay.pos(:,2); +datavector=abs(stdRatio); + +f2=figure; hold on; +ft_plot_lay(tmpLay, 'box', 'off'); +ft_plot_topo(chanX,chanY,datavector,... + 'interpmethod','v4',... + 'interplim','mask',... + 'gridscale',150,... + 'outline',tmpLay.outline,... + 'shading','flat',... + 'isolines',6,... + 'mask',tmpLay.mask,... + 'style','surfiso'); +axis equal; +hold on; +plot(tmpLay.pos(indBad,1),tmpLay.pos(indBad,2),'.k','markersize',30); +plot(tmpLay.pos(indBad,1),tmpLay.pos(indBad,2),'.w','markersize',15); +plot(tmpLay.pos(indBad,1),tmpLay.pos(indBad,2),'+r','markersize',20); +%axis([-0.6 0.6 -0.6 0.6]); +%axis off; +%%caxis([0.3 0.8]); +colorbar +title('Std Ratio with neighbours: cross at bad channels '); + + + + +fname1=[resultprefix,'_badchan_std_scatter.png']; +fname2=[resultprefix,'_badchan_std_topo.png']; + +hcp_write_figure(fname1, f1); +hcp_write_figure(fname2, f2); + +% zip(zipimagefile,{fname1,fname2}); +% delete(fname1) +% delete(fname2) + +close(f1); +close(f2); + diff --git a/analysis_functions/hcp_qc_zscore.m b/analysis_functions/hcp_qc_zscore.m new file mode 100644 index 0000000..e97fc4a --- /dev/null +++ b/analysis_functions/hcp_qc_zscore.m @@ -0,0 +1,341 @@ +function [badsegment_zscore, dataclean] = hcp_qc_zscore (datain, options_zscore) + +% This function identifies segments of bad data. Id does so in three steps. Step +% A: use the z-vale of the data one-sample difference. A threshold is defined by +% the used and all the points above this threshold are identified. Then they are +% clustered so that two consequent artifact points are no more than 500msec apart. +% By this procedure squid jumps are identified. Also muscle artifacts can be +% identified by this step. Step B: The data is searched for cliiping(channels with +% flat signal) by fieldtrip's cliiping artifact detection. Step C: his step is +% using fieltrip's muscle artifact detection. This procedure is optimized (data +% filtering) for muscle artifacts detection. Artifacts identified here might +% coincide with artifacts in stepA. At the end all bad segments from the three +% steps are concatanated in a singe sequence. Also the data is split in pseudo +% trials of 1 sec duration and the ones containg artifacts are excluded and the +% remaining clean dataset is passed at the output for subsequent use. +% +% INPUTS +% datain : This data structure should contain the +% entire dataset in one continous trial. +% +% options_correl: This is a row cell which key-value pairs in which the +% parameters for the neighbour correlation are set. These paramters +% are: +% +% resultprefix: This is a char containing the subject and +% experiment ID and which is used to name the plots created +% by this function +% +% zthreshold: The threshold of z-value of the one-sample +% data time derivative. The same threshold is used in step A +% and step C. +% +% OUPUTS: +% badsegment_zscore: This the bad segments definition matric. +% First column is the start sample and the second column is +% the end sample. +% +% dataclean : This is a data structure with pseudo trials of 1 sec duration. +% From this data structure the pseudo trials that coincide with artifacts are removed. + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +if length(datain.trial)>1 + error(['input dataset should have the entire data set in a single trial']); + return; +end + +zthreshold = ft_getopt(options_zscore, 'zthreshold'); % skipped intervals previously identified +skipped_intervals = ft_getopt(options_zscore, 'skipped_intervals') ; +%---Distance between successive artifacts for cluster definition + +Fsample=datain.fsample; +clusterStep=0.5; % msec +artifPad=0.1; +clusterStepSamps=floor(clusterStep*Fsample); % msec +artifPadSamps=floor(artifPad*Fsample); % msec + +%data1=ft_selectdata(datain,'channel',{'MEG','MEGREF','-AF*','-M*'}); +data1=datain; +Nchannels=length(data1.label); + +%============================================================= +% if skipped intervals have ben provided by the user then put nans there so +% they are not taking into consideration in the z-score computations +if ~isempty(skipped_intervals) + Nskipped=size(skipped_intervals,1); + for iSk=1:Nskipped, + data1.trial{1}(:,skipped_intervals(iSk,1):skipped_intervals(iSk,2))=nan; + end +end +%============================================================= + +%============================================================= +% Z value artifact identification. It also counts how many channels have +% benn over the threshold. It also uses a time window to define clusters +% of astrtifacts. +%=================================================== +diffData=abs(diff(data1.trial{1},1,2)); +meanDiff=nanmean(diffData,2); +stdDiff=nanstd(diffData,0,2); + +Ntimes=size(diffData,2); +diffData=(diffData-repmat(meanDiff,1,Ntimes))./repmat(stdDiff,1,Ntimes); + +zData=diffData; +clear diffData meanDiff stdDiff Ntimes; + +%{ +zData=diffData; +for iChan=1:Nchannels, + % iChan + zData(iChan,:)=(diffData(iChan,:)-meanDiff(iChan))./stdDiff(iChan); +end +%} + +[i1,j1] = find(zData>zthreshold); +%========================================================== +uniqChInd=unique(i1); +uniqTimeInd=unique(j1); +chanInfo=zeros(length(uniqChInd),2); +timeInfo=zeros(length(uniqTimeInd),2); +for iCh=1:length(uniqChInd) + chanInfo(iCh,1)=uniqChInd(iCh); + chanInfo(iCh,2)=sum(i1==uniqChInd(iCh)); +end +for iTime=1:length(uniqTimeInd) + timeInfo(iTime,1)=uniqTimeInd(iTime); + timeInfo(iTime,2)=sum(j1==uniqTimeInd(iTime)); +end +%------------------------------ +%------------------------------ +if ~isempty(timeInfo) + clusterInfo=[]; + countClust=1; + clustChanInd=[]; + clustChanInd{countClust}=[i1(j1==timeInfo(1,1))]'; + clusterInfo(countClust,:)=timeInfo(1,1)*[1 1]; + for iTime=1:size(timeInfo,1)-1, + tmpChanInd=i1(j1==timeInfo(iTime+1,1)); + if timeInfo(iTime+1,1)-timeInfo(iTime,1)<=clusterStepSamps + clusterInfo(countClust,2)=timeInfo(iTime+1,1); + clustChanInd{countClust}=unique([clustChanInd{countClust} tmpChanInd']); + else + countClust=countClust+1; + clustChanInd{countClust}=tmpChanInd'; + clusterInfo(countClust,:)=timeInfo(iTime+1,1)*[1 1]; + end + end + %------------------------------ + sinClustInfo=[]; + countSing=1; + for iClust=1:size(clusterInfo,1), + if length(clustChanInd{iClust})==1, + sinClustInfo(countSing,:)=[iClust clustChanInd{iClust}]; + countSing=countSing+1; + end + + end + dualClustInfo=[]; + countDual=1; + for iClust=1:size(clusterInfo,1), + if length(clustChanInd{iClust})==2, + dualClustInfo(countDual,:)=[iClust clustChanInd{iClust}]; + countDual=countDual+1; + end + end + + +else + + clusterInfo=[]; + +end + +NtotSamps=size(zData,2); +if ~isempty(clusterInfo) + for iClust=1:size(clusterInfo,1) + if (clusterInfo(iClust,1)-artifPadSamps) >0 + clusterInfo(iClust,1)=clusterInfo(iClust,1)-artifPadSamps; + end + if (clusterInfo(iClust,2)+artifPadSamps) < NtotSamps + clusterInfo(iClust,2)=clusterInfo(iClust,2)+artifPadSamps; + end + end +end +clear zData; +%================================================================ +%--- Perform Filetrip artifact detection for clipping and muscle +cfg=[]; +cfg.continuous='no'; +cfg.trl=[data1.sampleinfo 0];% This is done to overcome issue with ft_artifact_zvalue.m +%--- Settings for Clipping +cfg.artfctdef.clip.pretim = 0.01; % pre-artifact rejection-interval in seconds +cfg.artfctdef.clip.psttim = 0.01; % post-artifact rejection-interval in seconds +cfg.artfctdef.clip.thresh = 0.05; % minimum duration in seconds of a datasegment with consecutive identical samples to be considered as 'clipped' +%--- Settings for Muscle +cfg.artfctdef.muscle.cutoff = zthreshold; +cfg.artfctdef.muscle.trlpadding = 0; +cfg.artfctdef.muscle.fltpadding = 0; +cfg.artfctdef.muscle.artpadding = 0.01; +cfg.artfctdef.muscle.bpfilter = 'yes'; +cfg.artfctdef.muscle.bpfreq = [110 140]; +cfg.artfctdef.muscle.bpfiltord = 4; +cfg.artfctdef.muscle.bpfilttype = 'but'; +cfg.artfctdef.muscle.hilbert = 'yes'; +cfg.artfctdef.muscle.boxcar = 0.2; +%--------------------------------------------------- + +% Downsample to avoid huge memory consumption by hilbert transform +if data1.fsample>1200 + % Downsample to avoid huge memory consumption by hilbert transform + defDSratio=3; + downcfg=[]; + downcfg.resamplefs = data1.fsample./defDSratio; + downcfg.detrend ='no'; + datadown=ft_resampledata(downcfg,data1); + + cfg.trl=[1 length(datadown.time{1}) 0]; + + %--------------------------------------------------- + cfg.artfctdef.clip.channel=datadown.label; + [cfgClip,artClip]=ft_artifact_clip(cfg,datadown); + if ~isempty(artClip) % Recover + artClip=floor(artClip); + artClip=[(defDSratio*artClip(:,1))-(defDSratio-1) defDSratio*artClip(:,2)]; + end + %---------------------------------------------- + disp('FINDING MUSCLE ARTIFACTS'); + cfg.artfctdef.muscle.channel=datadown.label; + [cfgMuscle,artMuscle]=ft_artifact_muscle(cfg,datadown); + disp('DONE WITH FINDING MUSCLE ARTIFACTS'); + if ~isempty(artMuscle) % Recover + artMuscle=[(defDSratio*artMuscle(:,1))-(defDSratio-1) defDSratio*artMuscle(:,2)]; + end + clear datadown; +else + %--------------------------------------------------- + cfg.artfctdef.clip.channel=data1.label; + [cfgClip,artClip]=ft_artifact_clip(cfg,data1); + artClip=floor(artClip); + %---------------------------------------------- + + cfg.artfctdef.muscle.channel=data1.label; + [cfgMuscle,artMuscle]=ft_artifact_muscle(cfg,data1); +end +%======================================================== +% combine the bad segments in a structure +badsegall=[clusterInfo; artClip; artMuscle]; + +if ~isempty(badsegall) + [i1,j1]=find(badsegall(:,1)<1); + if ~isempty(i1), + badsegall(i1,1)=1; + end + [i1,j1]=find(badsegall(:,2)>data1.sampleinfo(2)); + if ~isempty(i1) + badsegall(i1,2)=data1.sampleinfo(2); + end + maxsmp = max(badsegall(:)); + badsmp = zeros(1,maxsmp); + for i=1:size(badsegall,1) + badsmp(badsegall(i,1):badsegall(i,2)) = 1; + end + badsmp = diff([0 badsmp 0]); + onset = find(badsmp== 1); + offset = find(badsmp==-1) - 1; + badsegment_zscore = [onset(:) offset(:)]; +else + badsegment_zscore = []; +end + +% try +% badsegall=[clusterInfo; artClip; artMuscle]; +% if ~isempty(badsegall) +% [i1,j1]=find(badsegall(:,1)<1); +% if ~isempty(i1), +% badsegall(i1,1)=1; +% end +% [i1,j1]=find(badsegall(:,2)>NtotSamps); +% if ~isempty(i1), +% badsegall(i1,1)=NtotSamps; +% end +% +% badsegall=sortrows(badsegall,1); +% +% dummyBin=zeros(1,badsegall(end,2)); +% for iBad=1:size(badsegall,1) +% dummyBin(badsegall(iBad,1):badsegall(iBad,2))=1; +% end +% dummyDif=[0 diff(dummyBin)]; +% artiStart=find(dummyDif>0); +% artiEnd=[find(dummyDif<0) badsegall(end,2)]; +% if length(artiStart)==length(artiEnd) +% badsegment_zscore=[artiStart' artiEnd']; +% elseif length(artiStart)-length(artiEnd)==1 +% badsegment_zscore=[artiStart(1:end-1)' artiEnd']; +% elseif length(artiStart)-length(artiEnd)==-1 +% badsegment_zscore=[[1 artiStart]' artiEnd']; +% end +% +% else +% badsegment_zscore=[]; +% end +% +% catch +% errorfile=['errorcase_',resultprefix]; +% fh=fopen(errorfile,'w+'); +% fprintf('%s',resultprefix); +% fclose(fh); +% end % try + +%======================================================== +% -- SPLIT DATA IN PSEUDO TRIALS OF ONE SECOND +%============================================= +defTrDur=1; %sec +defTrSamps=floor(defTrDur.*Fsample); +nSamples=size(data1.trial{1},2); + +Ntrials=floor(nSamples./defTrSamps); + +trlStart=defTrSamps.*((0:Ntrials-1)')+1; +trlEnd=defTrSamps.*((1:Ntrials)'); +trlDur=zeros(Ntrials,1); + +trl=[trlStart trlEnd trlDur]; + + +cfg=[]; +cfg.trl=trl; +data1=ft_redefinetrial(cfg,data1); + +cfg=[]; +cfg.demean='yes'; +data1=ft_preprocessing(cfg,data1); +%=================================================== + +%======================================================== +%---- Reject Artifact intervals +artCfg=[]; +artCfg.artfctdef.zvalue.artifact = [skipped_intervals; badsegment_zscore]; % remove maually set skipped intervals and identified bad segments +dataclean = ft_rejectartifact(artCfg, data1); clear data1; +%======================================= +clearvars -except badsegment_zscore dataclean + + + diff --git a/analysis_functions/hcp_read_ascii.m b/analysis_functions/hcp_read_ascii.m new file mode 100644 index 0000000..74306f4 --- /dev/null +++ b/analysis_functions/hcp_read_ascii.m @@ -0,0 +1,83 @@ +function value = hcp_read_ascii(filename) + +% HCP_READ_ASCII reads one or multiple MATLAB variable from an ASCII file. +% It works just like the standard MATLAB load command. Besides reading the +% variables, it also prints the MD5 checksum to screen. +% +% Use as +% hcp_read_ascii(filename) +% to load all variables from the text file into the local workspace, or as +% struct = hcp_read_ascii(filename) +% to load multiple variables from the text file into a single MATLAB structure. +% +% See also LOAD, HCP_WRITE_ASCII, HCP_READ_MATLAB, HCP_WRITE_MATLAB + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +filewithpath = hcp_which(filename); + +if isempty(filewithpath) + error('file "%s" does not exist', filename); +else + filename = filewithpath; + clear filewithpath +end + +[p, f, x] = fileparts(filename); +fprintf('reading file "%s" with md5sum %s\n', [f x], hcp_md5sum(filename)); +clear p f x + +% this function uses some local variables with a cryptical name to prevent +% variable name colisions with the local copy of the input variables. +filename_c9e61b166b = filename; +clear filename + +fid_c9e61b166b = fopen(filename_c9e61b166b, 'rt'); + +if fid_c9e61b166b<0 + error('cannot open file "%s"', filename_c9e61b166b); + +else + str_c9e61b166b = fread(fid_c9e61b166b, [1 inf], 'char=>char'); + fclose(fid_c9e61b166b); + + w1_c9e61b166b = []; % this variable has to exist just prior to "whos" to ensure that it gets included + w1_c9e61b166b = whos; + + evalc(str_c9e61b166b); + + % determine the new variables that were created by the evalc command + w2_c9e61b166b = whos; + name_c9e61b166b = setdiff({w2_c9e61b166b.name}, {w1_c9e61b166b.name}); + + if nargout==0 + for indx_c9e61b166b=1:length(name_c9e61b166b) + assignin('caller', name_c9e61b166b{indx_c9e61b166b}, eval(name_c9e61b166b{indx_c9e61b166b})); + end + + elseif nargout==1 + for indx_c9e61b166b=1:length(name_c9e61b166b) + value.(name_c9e61b166b{indx_c9e61b166b}) = eval(name_c9e61b166b{indx_c9e61b166b}); + end + + else + error('multiple output arguments not supported') + end + +end + diff --git a/analysis_functions/hcp_read_matlab.m b/analysis_functions/hcp_read_matlab.m new file mode 100644 index 0000000..ee6a48f --- /dev/null +++ b/analysis_functions/hcp_read_matlab.m @@ -0,0 +1,61 @@ +function value = hcp_read_matlab(filename, varargin) + +% HCP_READ_MATLAB reads one or multiple MATLAB variable from a *.mat file. +% It works just like the standard MATLAB load command. Besides reading the +% variables, it also prints prints the MD5 checksum to screen. +% +% Use as +% hcp_read_matlab(filename) +% to load all variables from the text file into the local workspace, or as +% struct = hcp_read_matlab(filename) +% to load multiple variables from the text file into a single MATLAB structure. +% +% See also LOAD, HCP_WRITE_MATLAB, HCP_READ_ASCII, HCP_WRITE_ASCII + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +filewithpath = hcp_which(filename); + +if isempty(filewithpath) + error('file "%s" does not exist', filename); +else + filename = filewithpath; + clear filewithpath +end + +[p, f, x] = fileparts(filename); +if ~strcmp(x, '.mat') + warning('appending the extension .mat'); + x = '.mat'; + filename = fullfile(p, [f x]); +end +fprintf('reading file "%s" with md5sum %s\n', [f x], hcp_md5sum(filename)); +clear p f x + +value = load(filename, varargin{:}); + +if ~nargout + % copy the variables into the caller workspace + fn = fieldnames(value); + for i=1:length(fn) + assignin('caller', fn{i}, value.(fn{i})); + end + clear value +else + % return the variables as a structure +end diff --git a/analysis_functions/hcp_scrubcallinfo.m b/analysis_functions/hcp_scrubcallinfo.m new file mode 100644 index 0000000..35e1a19 --- /dev/null +++ b/analysis_functions/hcp_scrubcallinfo.m @@ -0,0 +1,79 @@ +function data = hcp_scrubcallinfo(data) + +% HCP_SCRUBCALLINFO removes the data.s.callinfo field from data.s all previous +% fields. The callinfo contains the date and time at which the analysis was +% performed. Within the HCP we don't want to share details on when a specific +% analysis was performed, especially not if the analysis is done shortly after +% acquisition of a subject. +% +% Use as +% data = hcp_scrubcallinfo(data) +% +% This function is called by HCP_WRITE_MATLAB and HCP_WRITE_ASCII + +% Copyright (C) 2013-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +switch class(data) + case 'cell' + for i=1:numel(data) + if isstruct(data{i}) + data{i} = scrubcallinfo(data{i}); + end + end + case 'struct' + for i=1:numel(data) + data(i) = scrubcallinfo(data(i)); + end + case 'double' + if ishandle(data) + scrubfigure(data); + end + otherwise + % do nothing +end + +end % hcp_scrubcallinfo + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function s = scrubcallinfo(s) +% this function expects a structure or structure-array as input +if isfield(s, 'callinfo') + warning('removing callinfo'); + s = rmfield(s, 'callinfo'); +end +% s.previous is expected to be either a structure or a cell-array, use recursion to deal with it +% it might also be that a fieldtrip structure is nested in another non-fieldtrip structure, hence also test all other fields +for k=1:numel(s) + fn = fieldnames(s); + for j=1:numel(fn) + f = s(k).(fn{j}); + if isstruct(f) + f = scrubcallinfo(f); + elseif iscell(f) + for i=1:numel(f) + if isstruct(f{i}) + f{i} = scrubcallinfo(f{i}); + end % isstruct + end % for i, loop over cell-array + end % if struct or cell + s(k).(fn{j}) = f; + end % for j, loop over fieldnames +end % for k, loop over structure-array + +end % scrubcallinfo diff --git a/analysis_functions/hcp_select_datadir.m b/analysis_functions/hcp_select_datadir.m new file mode 100644 index 0000000..dd6b780 --- /dev/null +++ b/analysis_functions/hcp_select_datadir.m @@ -0,0 +1,43 @@ +function dir = hcp_select_datadir(dirlist) + +% HCP_SELECT_DATADIR checks for the presence of each directory from a list +% and returns the first from the list that is present. It helps in managing +% the HCP data on multiple computers. +% +% Use as +% dir = hcp_check_datadir(dirlist) +% where the input dirlist is a cell array with strings and the output is a +% single string. +% +% Example: +% +% dataprefix = hcp_check_datadir({ ... +% '/data/jansch/HCP/database' ... +% '/data/roboos/webdav' ... +% '/scratch/r.oostenveld/webdav' ... +% }); + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +dir = []; +for i=1:length(dirlist) + if isdir(dirlist{i}) + dir = dirlist{i}; + return + end +end diff --git a/analysis_functions/hcp_tfavg_contrasts.m b/analysis_functions/hcp_tfavg_contrasts.m new file mode 100644 index 0000000..fa82fad --- /dev/null +++ b/analysis_functions/hcp_tfavg_contrasts.m @@ -0,0 +1,973 @@ +function [outStatus] = hcp_tfavg_contrastsonthefly(inCfg) + +% This function loads time frequency data and computes its trial average as well +% as the average of its planar gradient +% +% outdatafile is the file where the averaged data will be saved +% outinfofile is the ZIP file where any plotted figures will be saved + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +outStatus=-1; + +%================================================ +% Get inputs +subjectid=inCfg.subjectid; +experimentid=inCfg.experimentid; +multiscanid=inCfg.multiscanid; % cell with the scan id of both files. They shoudl be in scannning order +bandinfo=inCfg.bandinfo; +contrastlist=inCfg.contrastlist; +avgmode=inCfg.avgmode; + +bands=bandinfo(:,1); +%================================================ +% Get basic information about input contrasts +Nfiles=length(contrastlist); % 1 (Motor) or 2(WM and SM) files +Ncontr=length(contrastlist{1}); % Check how many contrasts from the first file's contrasts + + +contrnames=[]; +datagroups=[]; +for iC=1:Ncontr, + contrnames{iC}=contrastlist{1}{iC}.mnemprint; + datagroups=unique([datagroups,contrastlist{1}{iC}.lockmode]); +end + +Ngroups=length(datagroups); + +%============================================================= +% Sort contralist list so that contrasts with the same datagroup of the +% first condition are in sequence. This is to avoid reloading data files +% all the time +sortcontrlist=[]; +countcontr=1; +for iGroup=1:Ngroups + for iC=1:Ncontr, + if strcmp(contrastlist{1}{iC}.lockmode{1},datagroups{iGroup}), + sortcontrlist{1}{countcontr}=contrastlist{1}{iC}; + if Nfiles==2 + sortcontrlist{2}{countcontr}=contrastlist{2}{iC}; + end + countcontr=countcontr+1; + end + end +end +sortcontrnames=[]; +sortgroupnames=[]; +isCompCntr=[]; % It says if it is a comparison between conditions +isDifGroups=[]; % It says if there are 2 different groups in each contrast +for iC=1:Ncontr, + sortcontrnames{iC}=sortcontrlist{1}{iC}.mnemprint; + sortgroupnames{iC}=sortcontrlist{1}{iC}.lockmode; + isCompCntr(iC,1)=length(sortgroupnames{iC})-1; + isDifGroups(iC,1)=length(unique(sortgroupnames{iC}))-1; +end +if Nfiles==2 + is2Files=1; +else + is2Files=0; +end +%============================================================= +% Now sort so that in each group if there is emgcoh all the emgcoh +% contrasts will be grouped together. +sortgroupsFirst=[]; +for iGC=1:Ncontr, + sortgroupsFirst{iGC}=sortgroupnames{iGC}{1}; +end +newsortcontrnames=sortcontrnames; +newsortgroupnames=sortgroupnames; +newsortcontrlist=cell(1,2); +for iGr=1:Ngroups, + [ind1,ind2]=match_str(sortgroupsFirst,datagroups{iGr}); + curList1=sortcontrlist{1}(ind1); + if is2Files, + curList2=sortcontrlist{2}(ind1); + end + newcurList1=[]; + nonemgList1=[]; + newcurList2=[]; + nonemgList2=[]; + countNew=1; + for indGC=1:length(curList1) + if strcmp(curList1{indGC}.connemetric,'emgcoh') + newcurList1{countNew}=curList1{indGC}; + if is2Files, + newcurList2{countNew}=curList2{indGC}; + end + countNew=countNew+1; + else + nonemgList1{end+1}=curList1{indGC}; + if is2Files, + nonemgList2{end+1}=curList2{indGC}; + end + end + end + newsortcontrlist{1}=[newsortcontrlist{1} newcurList1 nonemgList1]; + if is2Files, + newsortcontrlist{2}=[newsortcontrlist{2} newcurList2 nonemgList2]; + end +end + +newsortcontrnames=[]; +newsortgroupnames=[]; +for iC=1:Ncontr, + newsortcontrnames{iC}=newsortcontrlist{1}{iC}.mnemprint; + newsortgroupnames{iC}=newsortcontrlist{1}{iC}.lockmode; + newisCompCntr(iC,1)=length(newsortgroupnames{iC})-1; + newisDifGroups(iC,1)=length(unique(newsortgroupnames{iC}))-1; +end +sortcontrlist=newsortcontrlist; +sortcontrnames=newsortcontrnames; +sortgroupnames=newsortgroupnames; +isCompCntr=newisCompCntr; +isDifGroups=newisDifGroups; +%============================================================= +%============================================================= +%% LOOP OVER CONTRASTS +prevGroups={'' ''}; +curCM=[]; +prevCM=[]; +for iC=1:Ncontr + %curcont= + + tmpTok=tokenize(multiscanid{1},'-'); + scanmnem=tmpTok{2}; + %------- The following is just for the cases where the suffix "_Run1 or + %Run2" has been added to the scanid in order to differentiate between 2 + %different runs of the same paradigm. i.e. The way Robert has saved data in + %his database for subject CP10168. + indRunStr=regexp(scanmnem,'_Run'); + if ~isempty(indRunStr), + scanmnem=scanmnem(1:indRunStr(1)-1); + end + + curCM=sortcontrlist{1}{iC}.connemetric; + + saveFnameData=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + saveFnameImage=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']_plot.png']; + %================================== + % Load the time frequency data for the corresponding group and from both + % files + + NcurGroups=length(sortgroupnames{iC}); + prevMatchIndx=cell(1,NcurGroups); + for iGr1=1:NcurGroups, + tmpIndx=find(strcmp(sortgroupnames{iC},prevGroups)); + if ~isempty(tmpIndx) + prevMatchIndx{iGr1}=tmpIndx(1); + else + prevMatchIndx{iGr1}=[]; + end + end + if ~isempty(prevMatchIndx{1}) + eval(['tmpdata1A=alldata',num2str(prevMatchIndx{1}),'A;']); + eval(['tmpdata1B=alldata',num2str(prevMatchIndx{1}),'B;']); + else + tmpdata1A=[]; + tmpdata1B=[]; + end + if NcurGroups>1 + if ~isempty(prevMatchIndx{2}) + eval(['tmpdata2A=alldata',num2str(prevMatchIndx{2}),'A;']); + eval(['tmpdata2B=alldata',num2str(prevMatchIndx{2}),'B;']); + else + tmpdata2A=[]; + tmpdata2B=[]; + end + end + + + if isempty(tmpdata1A) + + genprefix = sprintf('%s_%s', experimentid, multiscanid{1}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{1}]; + load(tmpdatafile,'data'); + grad=data.grad; + %===================== + if data.fsample>2000 + newFs=data.fsample/4; + elseif data.fsample>1000 + newFs=data.fsample/2; + else + newFs=data.fsample; + end + cfg = []; + cfg.detrend = 'no'; + cfg.resamplefs = newFs; + data = ft_resampledata(cfg, data); % clear data; + + %========================= + alldata1A=data; clear data; + + if is2Files + genprefix = sprintf('%s_%s', experimentid, multiscanid{2}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{1}]; + load(tmpdatafile,'data'); + %===================== + if data.fsample>2000 + newFs=data.fsample/4; + elseif data.fsample>1000 + newFs=data.fsample/2; + else + newFs=data.fsample; + end + cfg = []; + cfg.detrend = 'no'; + cfg.resamplefs = newFs; + data = ft_resampledata(cfg, data); % clear data; + + %========================= + alldata1B=data;clear data; + else + alldata1B=[]; + end + else + alldata1A=tmpdata1A;clear tmpdata1A; + alldata1B=tmpdata1B;clear tmpdata1B; + end + + if isCompCntr(iC) + if isDifGroups(iC) + + if isempty(tmpdata2A) + genprefix = sprintf('%s_%s', experimentid, multiscanid{1}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{2}]; + load(tmpdatafile,'data'); + %===================== + if data.fsample>2000 + newFs=data.fsample/4; + elseif data.fsample>1000 + newFs=data.fsample/2; + else + newFs=data.fsample; + end + cfg = []; + cfg.detrend = 'no'; + cfg.resamplefs = newFs; + data = ft_resampledata(cfg, data); % clear data; + + %========================= + alldata2A=data; clear data; + if is2Files + genprefix = sprintf('%s_%s', experimentid, multiscanid{2}); tmpdatafile=[genprefix,'_tmegpreproc_',sortgroupnames{iC}{2}]; + load(tmpdatafile,'data'); + %===================== + if data.fsample>2000 + newFs=data.fsample/4; + elseif data.fsample>1000 + newFs=data.fsample/2; + else + newFs=data.fsample; + end + cfg = []; + cfg.detrend = 'no'; + cfg.resamplefs = newFs; + data = ft_resampledata(cfg, data); % clear data; + + %========================= + alldata2B=data;clear data; + else + alldata2B=[]; + end + + + else + alldata2A=tmpdata2A;clear tmpdata2A; + alldata2B=tmpdata2B;clear tmpdata2B; + end + + else + alldata2A=alldata1A; clear tmpdata2A; + alldata2B=alldata1B; clear tmpdata2B; + end + else + alldata2A=[]; + alldata2B=[]; + end + + %------------------------------------------------------ + % Get data from constrast conditions and merge from different files + sel=sortcontrlist{1}{iC}.selection{1}; + if ~isempty(sel) + datacntr1A=ft_selectdata(alldata1A,'rpt',sel); + else + datacntr1A=[]; + end + + if is2Files, + sel=sortcontrlist{2}{iC}.selection{1}; + + if ~isempty(sel) + datacntr1B=ft_selectdata(alldata1B,'rpt',sel); + if ~isempty(datacntr1A) + cfg=[]; % + [indChA,indChB]=match_str(datacntr1A.label,datacntr1B.label); datacntr1A=ft_selectdata(datacntr1A,'channel',indChA); datacntr1B=ft_selectdata(datacntr1B,'channel',indChB); + datacntr1=ft_appenddata(cfg,datacntr1A,datacntr1B); clear datacntr1A datacntr2A + else + datacntr1=datacntr1B; clear datacntr1B; + end + else + datacntr1=datacntr1A; clear datacntr1A; + end + else + datacntr1=datacntr1A; clear datacntr1A; + end + + if isempty(datacntr1) + errorFname=['NOTRIALSFOUND_',experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,'].txt']; + errorTime=clock; + errorCase=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + errorType=['No trials were found for condition 1']; + hcp_write_ascii(errorFname,'errorTime','errorCase','errorType'); + warning(['No trial found for condition 1 in ',experimentid,' - ',sortcontrlist{1}{iC}.mnemprint]) ; + continue + end + datacntr1.grad=grad; + %--------------------- + + if strcmp(curCM,'emgcoh') + emgRefChan=['EMG_',sortcontrlist{1}{iC}.mnemtrl{1}]; + end + + %-------------------------- + datacntr1_MEG=ft_selectdata(datacntr1,'channel',{'MEG'}); + if strcmp(curCM,'emgcoh') + datacntr1_EMG=ft_selectdata(datacntr1,'channel',emgRefChan); + else + datacntr1_EMG=[]; + end + + if strcmp(avgmode,'planar'), + if ~exist('neighbours','var') + cfg=[]; + cfg.method='distance'; + cfg.neighbourdist = 0.037; % (3.7 cm is the distance to 1st order neighbs) + neighbours = ft_prepare_neighbours(cfg, datacntr1); + end + + cfg = []; + cfg.feedback='no'; + cfg.planarmethod = 'sincos'; + cfg.neighbours= neighbours; + %tmpData.grad=grad; + datacntr1_MEG = ft_megplanar(cfg, datacntr1_MEG); + + end + clear datacntr1; + %-------------------------- + + if isCompCntr(iC) + sel=sortcontrlist{1}{iC}.selection{2}; + if ~isempty(sel) + datacntr2A=ft_selectdata(alldata2A,'rpt',sel); + else + datacntr2A=[]; + end + if is2Files, + sel=sortcontrlist{2}{iC}.selection{2}; + if ~isempty(sel) + datacntr2B=ft_selectdata(alldata2B,'rpt',sel); + if ~isempty(datacntr2A) + cfg=[]; % + [indChA,indChB]=match_str(datacntr2A.label,datacntr2B.label); datacntr2A=ft_selectdata(datacntr2A,'channel',indChA); datacntr2B=ft_selectdata(datacntr2B,'channel',indChB); + datacntr2=ft_appenddata(cfg,datacntr2A,datacntr2B); clear datacntr2A datacntr2B + else + datacntr2=datacntr2B; clear datacntr2B; + end + else + datacntr2=datacntr2A; clear datacntr2A; + end + else + datacntr2=datacntr2A; clear datacntr2A; + end + + + if isempty(datacntr2) + errorFname=['NOTRIALSFOUND_',experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,'].txt']; + errorTime=clock; + errorCase=[experimentid,'_',scanmnem,'_',sortcontrlist{1}{iC}.mnemprint,'_[MODE-',avgmode,']']; + errorType=['No trials were found for condition 2']; + hcp_write_ascii(errorFname,'errorTime','errorCase','errorType'); + warning(['No trial found for condition 2 in ',experimentid,' - ',sortcontrlist{1}{iC}.mnemprint]) ; + continue + end + + datacntr2.grad=grad; + %-------------------------- + datacntr2_MEG=ft_selectdata(datacntr2,'channel',{'MEG'}); + if strcmp(curCM,'emgcoh') + datacntr2_EMG=ft_selectdata(datacntr2,'channel',emgRefChan); + else + datacntr2_EMG=[]; + end + + if strcmp(avgmode,'planar'), + cfg = []; + cfg.feedback='no'; + cfg.planarmethod = 'sincos'; + cfg.neighbours= neighbours; + %tmpData.grad=grad; + datacntr2_MEG = ft_megplanar(cfg, datacntr2_MEG); + + end + %-------------------------- + clear datacntr2; + else + datacntr2_MEG=[]; + datacntr2_EMG=[]; + end + %------------------------------------------------------ + % Compute average for each condition + %========================================================================== + + % -------------- lower frequencies ----------------- % + foi1 = [1:1:30]; % frequencies from 2.5 to 29 Hz in 1 Hz steps + %0.5*ones(length(foi1),1)'; % time window: 500 ms + % -------------- higher frequencies ----------------- % + foi2 = [31:2:100]; % frequencies from 30 to 100 Hz in 2.5 Hz steps + %tw2=0.5*ones(length(foi2),1)'; % time window: 200 ms + % -------------- everything together ----------------- % + foi=[foi1 foi2]; + %tw=[tw1 tw2]; + tw=5./foi; + tw(tw>0.5)=0.5; + + + + %========================================================================== + freqCfg = []; + freqCfg.pad = 8; + freqCfg.toiStep = 0.025; + freqCfg.method = 'mtmconvol'; + freqCfg.taper = 'hanning'; + freqCfg.foi = foi; % analysis 2 to 30 Hz in steps of 2 Hz + freqCfg.t_ftimwin = tw; % length of time window = 0.5 sec + freqCfg.keeptrials = 'yes'; % important keeptrials + freqCfg.feedback = 'yes'; + %========================================================================== + + timeperiods1=sortcontrlist{1}{iC}.timeperiods{1}; + if isempty(timeperiods1) + timeperiods1=datacntr1_MEG.time{1}; + end + if isCompCntr(iC) + timeperiods2=sortcontrlist{1}{iC}.timeperiods{2}; + if isempty(timeperiods2) + timeperiods2=datacntr2_MEG.time{1}; + end + end + + + + + if strcmp(curCM,'emgcoh') + %emgRefChan=['EMG_',sortcontrlist{1}{iC}.mnemtrl{1}]; + + if strcmp(avgmode,'planar'), + + + freqCfg.output = 'fourier'; + freqCfg.toi=timeperiods1; + freqCfg.keeptrials='yes'; + freqCfg.feedback='text'; + + freqfour_MEG=ft_freqanalysis(freqCfg,datacntr1_MEG);clear datacntr1_MEG; + freqfour_EMG=ft_freqanalysis(freqCfg,datacntr1_EMG);clear datacntr1_EMG; + + combCfg=[]; + combCfg.combinemethod='svd'; + freqfour_MEG=ft_combineplanar(combCfg, freqfour_MEG); + %freqplanar.grad=grad; + + avg1=ft_freqdescriptives([],freqfour_MEG); + + cfg=[];cfg.parameter={'fourierspctrm','cumtapcnt'}; + freqfour_MEG=ft_appendfreq(cfg,freqfour_MEG,freqfour_EMG);clear freqfour_EMG; + + + + + cfg = []; + cfg.method = 'coh'; + cfg.channelcmb = {'MEG' emgRefChan}; + freqemgcoh = ft_connectivityanalysis(cfg, freqfour_MEG);clear freqfour_MEG; + + else + + datacntr1=ft_appenddata([],datacntr1_MEG,datacntr1_EMG);clear datacntr1_MEG datacntr1_EMG + freqCfg.output = 'powandcsd'; + freqCfg.toi=timeperiods1; + freqCfg.keeptrials='no'; + freqCfg.feedback='text'; + freqCfg.channelcmb = ft_channelcombination({'MEG' emgRefChan},datacntr1.label); + freqpowcsd=ft_freqanalysis(freqCfg,datacntr1); + tmpcontrdata1=ft_selectdata(freqpowcsd,'channel','MEG'); + avg1=ft_freqdescriptives([],tmpcontrdata1); clear tmpcontrdata1; + + cfg = []; + cfg.method = 'coh'; + cfg.channelcmb = ft_channelcombination({'MEG' emgRefChan},datacntr1.label); + freqemgcoh = ft_connectivityanalysis(cfg, freqpowcsd);clear freqpowcsd; + + end + + + [ind1,ind2]=match_str(avg1.label,freqemgcoh.labelcmb(:,1)); + + avg1.powspctrm=freqemgcoh.cohspctrm(ind2,:,:); clear freqemgcoh; + + else + + if strcmp(avgmode,'planar'), + + + freqCfg.output = 'fourier'; + freqCfg.toi=timeperiods1; + freqCfg.keeptrials='yes'; + freqCfg.feedback='text'; + + freqfour_MEG=ft_freqanalysis(freqCfg,datacntr1_MEG);clear datacntr1_MEG; + if ~isempty(datacntr1_EMG) + freqfour_EMG=ft_freqanalysis(freqCfg,datacntr1_EMG);clear datacntr1_EMG; + else + freqfour_EMG=[]; + end + combCfg=[]; + combCfg.combinemethod='svd'; + freqfour_MEG=ft_combineplanar(combCfg, freqfour_MEG); + %freqplanar.grad=grad; + + if ~isempty(freqfour_EMG) + cfg=[];cfg.parameter={'fourierspctrm','cumtapcnt'}; + freqfour_MEG=ft_appendfreq(cfg,freqfour_MEG,freqfour_EMG); + end + + avg1=ft_freqdescriptives([],freqfour_MEG); clear freqFour_MEG freqFour_EMG; + + else + if ~isempty(datacntr1_EMG) + datacntr1=ft_appenddata([],datacntr1_MEG,datacntr1_EMG);clear datacntr1_MEG datacntr1_EMG + else + datacntr1=datacntr1_MEG;clear datacntr1_MEG datacntr1_EMG + end + freqCfg.output = 'pow'; + freqCfg.toi=timeperiods1; + freqCfg.keeptrials='no'; + freqCfg.feedback='text'; + avg1=ft_freqanalysis(freqCfg,datacntr1);clear datacntr1; + + end + + + + + + if isCompCntr(iC) + if strcmp(avgmode,'planar'), + + + freqCfg.output = 'fourier'; + freqCfg.toi=timeperiods2; + freqCfg.keeptrials='yes'; + freqCfg.feedback='text'; + + freqfour_MEG=ft_freqanalysis(freqCfg,datacntr2_MEG);clear datacntr2_MEG; + if ~isempty(datacntr2_EMG) + freqfour_EMG=ft_freqanalysis(freqCfg,datacntr2_EMG);clear datacntr2_EMG; + else + freqfour_EMG=[]; + end + + combCfg=[]; + combCfg.combinemethod='svd'; + freqfour_MEG=ft_combineplanar(combCfg, freqfour_MEG); + %freqplanar.grad=grad; + + if ~isempty(freqfour_EMG) + cfg=[];cfg.parameter={'fourierspctrm','cumtapcnt'}; + freqfour_MEG=ft_appendfreq(cfg,freqfour_MEG,freqfour_EMG); + end + + avg2=ft_freqdescriptives([],freqfour_MEG); clear freqFour_MEG freqFour_EMG; + + else + if ~isempty(datacntr2_EMG) + datacntr2=ft_appenddata([],datacntr2_MEG,datacntr2_EMG);clear datacntr2_MEG datacntr2_EMG + else + datacntr2=datacntr2_MEG;clear datacntr2_MEG datacntr2_EMG + end + freqCfg.output = 'pow'; + freqCfg.toi=timeperiods2; + freqCfg.keeptrials='no'; + freqCfg.feedback='text'; + avg2=ft_freqanalysis(freqCfg,datacntr2);clear datacntr2; + + end + + end + end + + %------------------------------------------------------ + % Apply contrast comparison conditions + % --- Check Time + operation=sortcontrlist{1}{iC}.operation; + if isCompCntr(iC) + if strcmp(operation,'diff') + avg1.powspctrm=avg1.powspctrm-avg2.powspctrm; + elseif strcmp(operation,'relch') + avg1.powspctrm=(avg1.powspctrm-avg2.powspctrm)./avg1.powspctrm; + else + error('For comparisons between conditions the supported operations are diff and relch') + end + end + %-------------------------------------------------------- + % PLOT + + if ~isCompCntr(iC) + if ~strcmp(curCM,'emgcoh') + baseline=sortcontrlist{1}{iC}.baseline{1}; + if isempty(baseline) + baseline=[-0.5 0]; % se a default baseline + end + cfg=[]; + cfg.baseline = baseline; + cfg.baselinetype = 'relchange'; + avg2plot=ft_freqbaseline(cfg,avg1); + else + avg2plot=avg1; + + end + + else + avg2plot=avg1; + + end + + data=avg1; + hcp_write_matlab(saveFnameData,'data'); + + plotsensmainareas(sortcontrlist{1}{iC},avg2plot,bandinfo,experimentid,scanmnem,saveFnameImage,avgmode); + + prevGroups=sortgroupnames{iC}; + + +end + + + +end +%--- END OF MAIN -------------------------- +%===================================================== +%===================================================== +%===================================================== +%% +function[allfreqpow]=loadsingallfreqpow(experimentid, scanid,groupname,bands,inavgmode) +allfreqpow=[]; +for iBand=1:length(bands) + genprefix = sprintf('%s_%s', experimentid, scanid); + if strcmp(inavgmode,'mag') + freqfile=[genprefix,'_tfsens_',groupname,'_',bands{iBand}]; + hcp_read_matlab(freqfile,'freq'); + elseif strcmp(inavgmode,'planar') + freqfile=[genprefix,'_tfsens_',groupname,'_',bands{iBand},'_planar']; + hcp_read_matlab(freqfile,'freqplanar'); + freq=freqplanar; clear freqplanar; + end + cfg=[];cfg.keeptrials='yes'; + freqpow=ft_freqdescriptives(cfg,freq); + if iBand==1 + allfreqpow=freqpow; + else + cfg=[]; cfg.parameter={'powspctrm'};%,'cumtapcnt'}; + allfreqpow=ft_appendfreq(cfg,allfreqpow,freqpow); + end +end +end +%% +function[allfreqfour]=loadsingallfreqfour(experimentid, scanid,groupname,bands,inavgmode) +allfreqfour=[]; +allcumtapcnt=[]; +for iBand=1:length(bands) + genprefix = sprintf('%s_%s', experimentid, scanid); + if strcmp(inavgmode,'mag') + freqfile=[genprefix,'_tfsens_',groupname,'_',bands{iBand}]; + hcp_read_matlab(freqfile,'freq'); + elseif strcmp(inavgmode,'planar') + freqfile=[genprefix,'_tfsens_',groupname,'_',bands{iBand},'_planar']; + hcp_read_matlab(freqfile,'freqplanar'); + freq=freqplanar; clear freqplanar; + end + + + if iBand==1 + allfreqfour=freq; + allcumtapcnt=freq.cumtapcnt; + else + allcumtapcnt=[allcumtapcnt freq.cumtapcnt]; + cfg=[];cfg.parameter={'fourierspctrm'}; + allfreqfour=ft_appendfreq(cfg,allfreqfour,freq); clear freq; + + end + allfreqfour.cumtapcnt=allcumtapcnt; +end +end + +%% +function[]=plotsensmainareas(incontrast,inavg,bandinfo,experimentid,scanmnem,saveFileName,avgmode) + +channels=[]; +channels.occ = {'A136', 'A137', 'A138', 'A163', 'A164', 'A165', 'A166', 'A185', 'A186', 'A187', 'A203', 'A204', 'A205', 'A219', 'A220', 'A221', 'A238', 'A239'}; +channels.occ_L = {'A134', 'A161', 'A182', 'A183', 'A199', 'A200', 'A201', 'A216', 'A217', 'A218', 'A235'}; +channels.occ_R = {'A107', 'A139', 'A140', 'A166', 'A167', 'A168', 'A188', 'A189', 'A190', 'A205', 'A206', 'A207', 'A222', 'A223'}; +channels.temppf_L = {'A96', 'A97', 'A98', 'A127', 'A128', 'A129', 'A130', 'A155', 'A156', 'A157', 'A179', 'A196'}; +channels.temppf_R = {'A112', 'A113', 'A144', 'A145', 'A146', 'A147', 'A148', 'A171', 'A172', 'A173', 'A174', 'A193', 'A209', 'A210', 'A211', 'A227', 'A247'}; +channels.front = {'A37', 'A38', 'A60', 'A61', 'A62', 'A63', 'A87', 'A88', 'A89', 'A90', 'A91', 'A92', 'A117', 'A118', 'A119', 'A120', 'A123', 'A124', 'A149', 'A150', 'A151'}; +channels.motor_L = {'A7', 'A9', 'A10', 'A11', 'A22', 'A23', 'A24', 'A25', 'A26', 'A41', 'A42', 'A43', 'A44', 'A45', 'A66', 'A67', 'A68', 'A69', 'A70'}; +channels.motor_R ={'A13', 'A14', 'A15', 'A16', 'A17', 'A30', 'A31', 'A32', 'A33', 'A34', 'A53', 'A54', 'A55', 'A56', 'A57', 'A80', 'A81', 'A82', 'A83', 'A84'}; +channels.pariet= {'A49', 'A73', 'A74', 'A75', 'A76', 'A77', 'A102', 'A103', 'A104', 'A105', 'A106', 'A107', 'A134', 'A135', 'A139', 'A162'}; + + + + +%========================================================================== +%========================================================================== +% Make figure outline +h1=figure(); +set(h1,'papertype','A3'); +set(h1,'paperunits','centimeters') +papersize=get(h1,'PaperSize'); +paperposition = [1 1 papersize-1 ]; + + +bigFonts1=floor(min(papersize)*0.25); +bigFonts2=floor(min(papersize)*0.5); +bigFonts3=floor(min(papersize)*1); +bigFonts4=floor(min(papersize)*2); + +set(h1,'papersize',papersize); +set(h1,'paperposition',paperposition); +set(h1,'position',[10 10 15*papersize]); +%--------------------------------------------- +Aha(1,1)=axes(); +set(Aha(1,1),'position',[0.1 0.625 0.2 0.1]); +Aha(1,2)=axes(); +set(Aha(1,2),'position',[0.375 0.625 0.2 0.1]); +Aha(1,3)=axes(); +set(Aha(1,3),'position',[0.65 0.625 0.2 0.1]); +%---- +Aha(2,1)=axes(); +set(Aha(2,1),'position',[0.1 0.475 0.2 0.1]); +Aha(2,2)=axes(); +set(Aha(2,2),'position',[0.375 0.475 0.2 0.1]); +Aha(2,3)=axes(); +set(Aha(2,3),'position',[0.65 0.475 0.2 0.1]); +%---- +Aha(3,1)=axes(); +set(Aha(3,1),'position',[0.1 0.325 0.2 0.1]); +Aha(3,2)=axes(); +set(Aha(3,2),'position',[0.375 0.325 0.2 0.1]); +Aha(3,3)=axes(); +set(Aha(3,3),'position',[0.65 0.325 0.2 0.1]); +%======================================================= +%--------------------------------------------- +Bha(1)=axes(); +set(Bha(1),'position',[0.05 0.15 0.15 0.10],'YTick',[],'XTick',[]); +Bha(2)=axes(); +set(Bha(2),'position',[0.225 0.15 0.15 0.10],'YTick',[],'XTick',[]); +Bha(3)=axes(); +set(Bha(3),'position',[0.4 0.15 0.15 0.10],'YTick',[],'XTick',[]); +Bha(4)=axes(); +set(Bha(4),'position',[0.575 0.15 0.15 0.10],'YTick',[],'XTick',[]); +%---- + +Bha(5)=axes(); +set(Bha(5),'position',[0.05 0.01 0.15 0.10],'YTick',[],'XTick',[]); +Bha(6)=axes(); +set(Bha(6),'position',[0.225 0.01 0.15 0.10],'YTick',[],'XTick',[]); +Bha(7)=axes(); +set(Bha(7),'position',[0.4 0.01 0.15 0.10],'YTick',[],'XTick',[]); +Bha(8)=axes(); +set(Bha(8),'position',[0.575 0.01 0.15 0.10],'YTick',[],'XTick',[]); +%---- +%======================================================= +%======================================================= +%======================================================= + + +%plot +%---- +axes(Aha(1,1)); +tmpavg=ft_selectdata(inavg,'channel',channels.temppf_L,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Left Temp-PF sens','fontsize',bigFonts2); +%---- +axes(Aha(1,2)); +tmpavg=ft_selectdata(inavg,'channel',channels.front,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Frontal sens','fontsize',bigFonts2); +%---- +axes(Aha(1,3)); +tmpavg=ft_selectdata(inavg,'channel',channels.temppf_R,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Right Temp-PF sens','fontsize',bigFonts2); +%--------------------------------------------------- +axes(Aha(2,1)); +tmpavg=ft_selectdata(inavg,'channel',channels.motor_L,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Left Motor sens','fontsize',bigFonts2); +%---- +axes(Aha(2,2)); +tmpavg=ft_selectdata(inavg,'channel',channels.pariet,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Parietal sens','fontsize',bigFonts2); +%---- +axes(Aha(2,3)); +tmpavg=ft_selectdata(inavg,'channel',channels.motor_R,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Right Motor sens','fontsize',bigFonts2); +%--------------------------------------------------- +axes(Aha(3,1)); +tmpavg=ft_selectdata(inavg,'channel',channels.occ_L,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Left Occ. sens','fontsize',bigFonts2); +%---- +axes(Aha(3,2)); +tmpavg=ft_selectdata(inavg,'channel',channels.occ,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Med. Occ. sens','fontsize',bigFonts2); +%---- +axes(Aha(3,3)); +tmpavg=ft_selectdata(inavg,'channel',channels.occ_R,'avgoverchan','yes');colorbar off; +minVal=min(tmpavg.powspctrm(:)); maxVal=max(tmpavg.powspctrm(:)); if sign(minVal)~=sign(maxVal), topozlim=max(abs([minVal maxVal]))*[-1 1]; else topozlim=[minVal maxVal];end +plotCfg=[]; plotCfg.zlim=topozlim; plotCfg.renderer ='painters';plotCfg.layout='4D248.lay'; plotCfg.ylim=[4 80];ft_singleplotTFR(plotCfg, tmpavg);colorbar off; +title('TFR avg of Right Occ. sens','fontsize',bigFonts2); +%---- +%--------------------------------------------------- + + +bandStr=[]; +for iBand=1:size(bandinfo,1) + iBand + axes(Bha(iBand)); + tmpfreqlim=bandinfo{iBand,2}; + tmpavg=ft_selectdata(inavg,'foilim',tmpfreqlim);colorbar off; + tmpavg=ft_selectdata(tmpavg,'avgovertime','yes'); + %------ + minVal=min(tmpavg.powspctrm(:)); + maxVal=max(tmpavg.powspctrm(:)); + if sign(minVal)~=sign(maxVal), + topozlim=max(abs([minVal maxVal]))*[-1 1]; + else + topozlim=[minVal maxVal]; + end + %------ + plotCfg=[]; + plotCfg.layout='4D248.mat'; + plotCfg.interpmethod='linear'; + plotCfg.marker='on'; + plotCfg.markersymbol='.'; + plotCfg.gridscale=50; + plotCfg.shading='flat'; + plotCfg.comment='no'; + + plotCfg.zlim=topozlim; + + ft_topoplotTFR(plotCfg, tmpavg); + colorbar off; + title(bandinfo{iBand,1},'fontsize',bigFonts2); + bandStr = [bandStr, sprintf('%s : %s to %s Hz\n',bandinfo{iBand,1},num2str(bandinfo{iBand,2}(1)),num2str(bandinfo{iBand,2}(2))) ]; +end +%{ +dispStr=sprintf('Bands: \n%s',bandStr); + +txtBoxBand = uicontrol('style','listbox'); +set(txtBoxBand,'units','normalized') +set(txtBoxBand,'Position',[0.75 0.01 0.2 0.25]); +set(txtBoxBand,'String',dispStr) +set(txtBoxBand,'Fontsize',6) +set(txtBoxBand,'HorizontalAlignment','left') + +%------------------------------------------------------------------------- +print(h1,'-dpng','-r600','testTFmulti1.png'); + + + +Bha(8)=axes(); +set(Bha(8),'position',[0.575 0.01 0.15 0.10],'YTick',[],'XTick',[]); +%} +dispStr=sprintf('Bands: \n%s',bandStr); +txtH=axes();axis off; +set(txtH,'position',[0.75 0.01 0.24 0.1],'YTick',[],'XTick',[]); +xlim=get(txtH,'Xlim');ylim=get(txtH,'Ylim'); +ht1=text(xlim(1),ylim(2),dispStr); +set(ht1,'Fontsize',bigFonts2) + +Toph1=axes(); +set(Toph1,'position',[0.1 0.95 0.8 0.05]);axis off +dispStringTop1=sprintf('%s\n%s\n%s',[' experimentid: ',regexprep(experimentid,'_','\\_'),' scan: ',regexprep(scanmnem,'_','\\_')],[' contrast: ',regexprep(incontrast.mnemprint,'_','\\_')],[' avgmode: ',regexprep(avgmode,'_','\\_')]); +htTop1=text(0,0.2,dispStringTop1); +set(htTop1,'Fontsize',bigFonts2) +%{ +Toph2=axes(); +set(Toph2,'position',[0.1 0.775 0.4 0.15]); axis off; +htTop2=text(0,0,[' contrast:',incontrast.mnemprint]); +%} +%print(h1,'-dpng',[saveFileName]); +hcp_write_figure(saveFileName, h1,'format','png'); +close(h1); +%========================================================================== +% ATTENTION !!! THE FOLLOWING PLOT ASSUMES THAT YOU HAVE A VERY LARGE VNC +% SCREEN . OTHERWISE VNC DIES. +%{ +[PATHSTR,NAME,EXT] = fileparts(saveFileName); + +saveFileName2=[PATHSTR,NAME,'_multiTFR',EXT]; +h2=figure; +%set(0,'Units','centimeters'); +%screenSize=get(0,'screensize'); +%if screenSize(3)>120, +% set(h2,'papertype','A2');%A2 +%else + set(h2,'papertype','A3');%A3 +%end +set(h2,'paperunits','centimeters') +%set(h2,'papersize',[59.4000 42]);%A2 +papersize=get(h2,'PaperSize'); +paperposition = [1 1 papersize-1 ]; +set(h2,'papersize',papersize); +set(h2,'paperposition',paperposition); +set(h2,'position',[10 10 15*papersize]); + +cfg=[]; +cfg.layout='4D248.mat'; +cfg.showoutline = 'yes'; +ft_multiplotTFR(cfg,inavg); + + +set(h2,'paperorientation','portrait'); + +hcp_write_figure(saveFileName2, h2,'format','png'); +close(h2); +%} +%========================================================================== +end diff --git a/analysis_functions/hcp_which.m b/analysis_functions/hcp_which.m new file mode 100644 index 0000000..f5353b6 --- /dev/null +++ b/analysis_functions/hcp_which.m @@ -0,0 +1,41 @@ +function filename = hcp_which(original) + +% HCP_WHICH is a replacement function for the builtin MATLAB which +% function that works in deployed mode. + +if ~isempty(dir(original)) + % the file exists as it is, no need for any changes + filename = original; + return +end + +% try to find the file on the path +p = tokenize(path, ':'); + +for i=1:length(p) + filename = fullfile(p{i}, original); + if ~isempty(dir(filename)) + break + end +end + +% still not found, try it with a *.m extension +for i=1:length(p) + filename = fullfile(p{i}, [original '.m']); + if ~isempty(dir(filename)) + break + end +end + +% still not found, try it with a *.mat extension +for i=1:length(p) + filename = fullfile(p{i}, [original '.mat']); + if ~isempty(dir(filename)) + break + end +end + +% still not found, return the original name +filename = original; +warning('could not find the file "%s" on the path', filename); + diff --git a/analysis_functions/hcp_write_ascii.m b/analysis_functions/hcp_write_ascii.m new file mode 100644 index 0000000..ad49a13 --- /dev/null +++ b/analysis_functions/hcp_write_ascii.m @@ -0,0 +1,88 @@ +function hcp_write_ascii(filename, varargin) + +% HCP_WRITE_ASCII writes one or multiple MATLAB variables to an ASCII file. +% It works just like the standard MATLAB save command, but also stores the +% correspoding providence information in an XML file. +% +% Use as +% hcp_write_ascii filename variable +% hcp_write_ascii filename variable1 variable2 ... +% or in the functional form as +% hcp_write_ascii(filename, name1, name2, ...) +% where the input consists of strings pointing to the variables that should be saved. +% +% See also SAVE, HCP_READ_ASCII, HCP_WRITE_MATLAB, HCP_WRITE_FIGURE, HCP_WRITE_PROVENANCE + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% this function uses some local variables with a cryptical name to prevent +% variable name colisions with the local copy of the input variables. +filename_c9e61b166b = filename; clear filename + +[p, f, x] = fileparts(filename_c9e61b166b); +if ~strcmp(x, '.txt') + warning('appending the extension .txt'); + x = '.txt'; + filename_c9e61b166b = fullfile(p, [f x]); +end +clear p f x + +if nargin>2 && nargin<=4 && ischar(varargin{1}) && ~ischar(varargin{2}) + % the old function call was hcp_write_ascii(filename, name, value, comment) + % this detection is not 100% foolproof + + name = varargin{1}; + value = varargin{2}; + % comment = varargin{4}; % this is now ignored + + fid_c9e61b166b = fopen(filename_c9e61b166b, 'wt'); + if fid_c9e61b166b<0 + error('cannot open file "%s"', filename_c9e61b166b); + end + fprintf(fid_c9e61b166b, printstruct(name, value)); + fclose(fid_c9e61b166b); + +else + + % copy the input variables from the caller into the local workspace + varname_c9e61b166b = cell(size(varargin)); + for index_c9e61b166b=1:length(varargin) + setvariable(varargin{index_c9e61b166b}, hcp_scrubcallinfo(evalin('caller', varargin{index_c9e61b166b}))); + end + clear index_c9e61b166b + + fid_c9e61b166b = fopen(filename_c9e61b166b, 'wt'); + if fid_c9e61b166b<0 + error('cannot open file "%s"', filename_c9e61b166b); + end + + for index_c9e61b166b=1:length(varargin) + fprintf(fid_c9e61b166b, printstruct(varargin{index_c9e61b166b}, eval(varargin{index_c9e61b166b}))); + end + fclose(fid_c9e61b166b); + +end + +% write the corresponding provenance information +hcp_write_provenance(filename_c9e61b166b); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION to prevent an eval(sprintf(...)) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function setvariable(name, val) +assignin('caller', name, val) diff --git a/analysis_functions/hcp_write_figure.m b/analysis_functions/hcp_write_figure.m new file mode 100644 index 0000000..d36fbb9 --- /dev/null +++ b/analysis_functions/hcp_write_figure.m @@ -0,0 +1,93 @@ +function hcp_write_figure(filename, varargin) + +% HCP_WRITE_FIGURE prints a MATLAB figure to a *.png file. It works just +% like the standard "print -dpng" command, but also stores the corresponding +% providence information in an XML file.. +% +% Use as +% hcp_write_figure(filename, handle, ...) +% where the input is a figure handle, or +% hcp_write_figure(filename, ...) +% to export the current figure to a bitmap file. +% +% Optional input arguments should be specified as key-value pairs +% and may include +% 'resolution' = number (default = 600) +% 'format' = string, can be png, fig, svg (default is all formats) +% +% It is also possible to specify the format as a cell-array to export it +% to multiple formats. +% +% See also HCP_WRITE_ASCII, HCP_WRITE_MATLAB, HCP_WRITE_PROVENANCE + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +if isempty(varargin) + handle = gcf; +elseif ~ishandle(varargin{1}) + handle = gcf; +elseif ishandle(varargin{1}) + handle = varargin{1}; + varargin = varargin(2:end); +end + +[p, f, x] = fileparts(filename); + +resolution = ft_getopt(varargin, 'resolution', 600); +if ~isempty(x) + format = ft_getopt(varargin, 'format', x(2:end)); % without the '.' +else + % set the default if nothing otherwise is specified + % format = ft_getopt(varargin, 'format', {'png', 'svg', 'fig'}); + format = ft_getopt(varargin, 'format', 'png'); +end + +if ~iscell(format) + format = {format}; +end + +if any(strcmp(format, 'png')) + filename = fullfile(p, [f '.png']); % ensure that it has the right extension + % first write it to a temporary file + tempfile = [tempname '.png']; + print(handle, '-dpng', sprintf('-r%d', resolution), tempfile); + % the MATLAB written file has the date of creating in it as metadata + % see https://github.com/Washington-University/megconnectome/issues/126 + % the solution is to read the RGB values and write them again without metadata + fprintf('scrubbing metadata from "%s"\n', tempfile); + rgb = imread(tempfile); + delete(tempfile); + savepng(rgb, filename, 4095); % use maximal compression + % write the corresponding provenance information + hcp_write_provenance(filename); +end + +if any(strcmp(format, 'fig')) + filename = fullfile(p, [f '.fig']); % ensure that it has the right extension + saveas(handle, filename, 'fig') + % write the corresponding provenance information + hcp_write_provenance(filename); +end + +if any(strcmp(format, 'svg')) + ft_hastoolbox('plot2svg', 1); + filename = fullfile(p, [f '.svg']); % ensure that it has the right extension + plot2svg(filename, handle); + % write the corresponding provenance information + hcp_write_provenance(filename); +end diff --git a/analysis_functions/hcp_write_matlab.m b/analysis_functions/hcp_write_matlab.m new file mode 100644 index 0000000..2b05766 --- /dev/null +++ b/analysis_functions/hcp_write_matlab.m @@ -0,0 +1,68 @@ +function hcp_write_matlab(filename, varargin) + +% HCP_WRITE_MATLAB writes one or multiple MATLAB variables to a *.mat file. +% It works just like the standard MATLAB save command, but also stores the +% correspoding providence information in an XML file. +% +% Use as +% hcp_write_matlab filename variable +% hcp_write_matlab filename variable1 variable2 ... +% or in the functional form as +% hcp_write_matlab(filename, name1, name2, ...) +% where the input consists of strings pointing to the variables that should be saved. +% +% See also SAVE, HCP_READ_MATLAB, HCP_WRITE_ASCII, HCP_WRITE_FIGURE, HCP_WRITE_PROVENANCE + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% this function uses some local variables with a cryptical name to prevent +% variable name colisions with the local copy of the input variables. +filename_c9e61b166b = filename; +clear filename + +[p, f, x] = fileparts(filename_c9e61b166b); +if ~strcmp(x, '.mat') + warning('appending the extension .mat'); + x = '.mat'; + filename_c9e61b166b = fullfile(p, [f x]); +end +clear p f x + +if isempty(varargin) + varargin = evalin('caller', 'whos'); + varargin = {varargin.name}; +end + +% get the anonymous input variables into the local workspace +for index_c9e61b166b=1:length(varargin) + if ~strcmp(varargin{index_c9e61b166b}(1),'-') + setvariable(varargin{index_c9e61b166b}, hcp_scrubcallinfo(evalin('caller', varargin{index_c9e61b166b}))); + end +end +clear index_c9e61b166b + +save(filename_c9e61b166b, '-v7', varargin{:}); + +% write the corresponding provenance information +hcp_write_provenance(filename_c9e61b166b); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION to prevent an eval(sprintf(...)) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function setvariable(name, val) +assignin('caller', name, val) diff --git a/analysis_functions/hcp_write_provenance.m b/analysis_functions/hcp_write_provenance.m new file mode 100644 index 0000000..aa80315 --- /dev/null +++ b/analysis_functions/hcp_write_provenance.m @@ -0,0 +1,61 @@ +function hcp_write_provenance(filename) + +% HCP_WRITE_PROVENANCE writes an XML file with provenance information to +% complement a MATLAB or an ascii file. Thit function is called automatically +% immediately following the writing of a MATLAB or ascii file. +% +% Use as +% hcp_write_provenance(filename) +% +% The filename should refer to the *.mat or *.txt file. The provenance +% information will be saved in a file with corresponding name but with the +% extension *.prov.xml +% +% See also HCP_WRITE_MATLAB, HCP_WRITE_ASCII + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +global hcp_default + +% the expected input filename extension is *.txt, *.mat or *.png +[p, f, x] = fileparts(filename); + +% get the general provenance information (e.g. user and hostname) +prov = hcp_provenance; + +% when called from the megconnectome application, it passes some information about the script that is executed +if isfield(hcp_default, 'prov') && isfield(hcp_default.prov, 'script') && isfield(hcp_default.prov.script, 'filename') + prov.script.filename = hcp_default.prov.script.filename; +end +if isfield(hcp_default, 'prov') && isfield(hcp_default.prov, 'script') && isfield(hcp_default.prov.script, 'md5sum') + prov.script.md5sum = hcp_default.prov.script.md5sum; +end + +% get the specific provenance information for the file that is written +prov.filename = [f x]; % filename with extension, exclude path +prov.md5sum = hcp_md5sum(filename); + +fprintf('writing file "%s" with md5sum %s\n', prov.filename, prov.md5sum); + +% huisdier.soort = 'kanarie'; +% huisdier.naam = 'geeltje'; +% huisdier.geboortedatum = '2012-10-11'; +% xml = struct2xml(struct('huisdier', huisdier)); + +fileexchange_struct2xml(struct('megconnectome', prov), filename); + diff --git a/analysis_functions/private/gethostname.m b/analysis_functions/private/gethostname.m new file mode 100644 index 0000000..df6ec6d --- /dev/null +++ b/analysis_functions/private/gethostname.m @@ -0,0 +1,55 @@ +function host = gethostname() + +% HOSTNAME +% +% Use as +% str = hostname; + +% Copyright (C) 2011-2014, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id: gethostname.m 6746 2012-10-11 19:10:06Z roboos $ + +% this is to speed up subsequent calls +persistent previous_argout +if ~isempty(previous_argout) + host = previous_argout; + return +end + +if (ispc()) + host = getenv('ComputerName'); +elseif (isunix()) + host = getenv('HOSTNAME'); + % the HOSTNAME variable is not guaranteed to be set + if isempty(host) + [status, host] = system('hostname -s'); + % remove the ^n from the end of the line + host = deblank(host); + end +end + +host = strtok(host, '.'); % dots in filenames are not allowed by matlab + +if (isempty(host)) + host = 'unknownhost'; +end + +% remember for subsequent calls +previous_argout = host; + diff --git a/analysis_functions/private/getusername.m b/analysis_functions/private/getusername.m new file mode 100644 index 0000000..c726371 --- /dev/null +++ b/analysis_functions/private/getusername.m @@ -0,0 +1,47 @@ +function user = getusername() + +% GETUSERNAME +% +% Use as +% str = username; + +% Copyright (C) 2011-2014, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id: getusername.m 6746 2012-10-11 19:10:06Z roboos $ + +% this is to speed up subsequent calls +persistent previous_argout +if ~isempty(previous_argout) + user = previous_argout; + return +end + +if (ispc()) + user = getenv('UserName'); +else + user = getenv('USER'); +end + +if (isempty(user)) + user = 'unknown'; +end + +% remember for subsequent calls +previous_argout = user; + diff --git a/bin/hcp_anatomy.sh b/bin/hcp_anatomy.sh new file mode 100755 index 0000000..ea95468 --- /dev/null +++ b/bin/hcp_anatomy.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#export DISPLAY="" + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +#MEGCONNECTOME_ROOT=$HOME/projects/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_anatomy + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + + diff --git a/bin/hcp_baddata.sh b/bin/hcp_baddata.sh new file mode 100755 index 0000000..e5685f3 --- /dev/null +++ b/bin/hcp_baddata.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_baddata + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_datacheck.sh b/bin/hcp_datacheck.sh new file mode 100755 index 0000000..f2946e9 --- /dev/null +++ b/bin/hcp_datacheck.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_datacheck + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_eravg.sh b/bin/hcp_eravg.sh new file mode 100755 index 0000000..196a186 --- /dev/null +++ b/bin/hcp_eravg.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_eravg + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_icaclass.sh b/bin/hcp_icaclass.sh new file mode 100755 index 0000000..24f38bf --- /dev/null +++ b/bin/hcp_icaclass.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_icaclass + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_icaclass_qc.sh b/bin/hcp_icaclass_qc.sh new file mode 100755 index 0000000..a5ddf38 --- /dev/null +++ b/bin/hcp_icaclass_qc.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_icaclass_qc + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_megrelease.sh b/bin/hcp_megrelease.sh new file mode 100755 index 0000000..39c83c8 --- /dev/null +++ b/bin/hcp_megrelease.sh @@ -0,0 +1,513 @@ +#!/bin/sh +# +# This script rearranges the MEG data and pipeline results into +# an organization that is suited for release. +# +# Use as +# hcp_megrelease.sh [options] +# +# The inputdir should point to the location of a single subjects +# data, INCLUDING the experiment name. +# +# The outputdir should point to a directory where the release +# should be compiled, also INCLUDING the experiment name. +# +# The selection of specific types of data is done with +# --raw include the unprocessed raw data in the release +# --anatomy include the results from the anatomy pipeline +# --eprime include the EPrime log files in the release +# --datacheck include the specific pipeline results +# --baddata include the specific pipeline results +# --icaclass include the specific pipeline results +# --rmegpreproc include the specific pipeline results +# --tmegpreproc include the specific pipeline results +# --eravg include the specific pipeline results +# --tfavg include the specific pipeline results +# --powavg include the specific pipeline results +# The default behaviour when none of these options is specified +# is to include all types of data. +# +# The selection of specific types of scans is done with +# --noise include the noise measurements +# --restin include the resting state data (note the "g" missing) +# --wrkmem include the working memory task data +# --storym include the story-math task data +# --motort include the motor task data (note the "t" at the end) +# The default behaviour when none of these options is specified +# is to include all types of scans. +# +# For example +# hcp_megrelease.sh /HCP/BlueArc/meg_scratch/intradb/archive1/HCP_Phase2/arc001/177746_MEG /HCP/BlueArc/meg_scratch/release/177746_MEG +# to release everything, or +# hcp_megrelease.sh --raw --anatomy /HCP/BlueArc/meg_scratch/intradb/archive1/HCP_Phase2/arc001/177746_MEG /HCP/BlueArc/meg_scratch/release/177746_MEG +# to release the raw data and the anatomical models. +# +# A more elaborate example to release the raw and anatomical data for multiple subjects is +# +# for SUBJECT in 104820 122317 177746 500222 +# do +# INPUTDIR=/HCP/BlueArc/meg_scratch/intradb/archive1/HCP_Phase2/arc001/"$SUBJECT"_MEG +# OUTPUTDIR=/HCP/BlueArc/meg_scratch/release/"$SUBJECT" +# hcp_megrelease.sh --anatomy --raw $INPUTDIR $OUTPUTDIR +# done + +# the option parsing is based on http://mywiki.wooledge.org/BashFAQ/035 + +function warning { + echo WARNING: $* >&2 +} + +function error { + echo ERROR: $* >&2 + exit 1 +} + +function copy { + if [ ! -d $2 ]; then + error cannot write to output directory $2 + return + fi + + if [ ! -f "$1" ] ; then + warning $1 is not a regular file + return + fi + + if [ ! -r "$1" ] ; then + warning cannot read from file $1 + return + fi + + echo copying $1 to $2 + rsync -a $1 $2 +} + +# Reset all variables that might be set +raw="true" +anatomy="true" +eprime="true" + +datacheck="true" +baddata="true" +icaclass="true" +rmegpreproc="true" +tmegpreproc="true" +eravg="true" +tfavg="true" +powavg="true" + +noise="true" +restin="true" +wrkmem="true" +storym="true" +motort="true" + +# first loop, decide between default/all or explicit selection +for ARG in $@ +do + case $ARG in + --raw | --anatomy | --eprime | --datacheck | --baddata | --icaclass | --rmegpreproc | --tmegpreproc | --eravg | --tfavg | --powavg) + # an explicit subselection has been made for the data type + raw="false" + anatomy="false" + datacheck="false" + baddata="false" + icaclass="false" + rmegpreproc="false" + tmegpreproc="false" + eravg="false" + tfavg="false" + powavg="false" + ;; + + --noise | --restin | --wrkmem | --storym | --motort) + # an explicit subselection has been made for the scan type + noise="false" + restin="false" + wrkmem="false" + storym="false" + motort="false" + ;; + + *) + break + ;; + esac +done + +# second loop, parse all arguments +while : +do + case $1 in + -h | --help | -\?) + echo + cat $0 | head -n 51 | tail -n 49 | tr -d \# + echo + exit 0 # This is not an error, User asked help. Don't do "exit 1" + ;; + + --raw) + raw="true" + shift + ;; + + --anatomy) + anatomy="true" + shift + ;; + + --eprime) + eprime="true" + shift + ;; + + --datacheck) + datacheck="true" + shift + ;; + + --baddata) + baddata="true" + shift + ;; + + --icaclass) + icaclass="true" + shift + ;; + + --rmegpreproc) + rmegpreproc="true" + shift + ;; + + --tmegpreproc) + tmegpreproc="true" + shift + ;; + + --eravg) + eravg="true" + shift + ;; + + --tfavg) + tfavg="true" + shift + ;; + + --powavg) + powavg="true" + shift + ;; + + --noise) + noise="true" + shift + ;; + + --restin) + restin="true" + shift + ;; + + --wrkmem) + wrkmem="true" + shift + ;; + + --storym) + storym="true" + shift + ;; + + --motort) + motort="true" + shift + ;; + + --) # End of all options + shift + break + ;; + + -*) + warning "Unknown option (ignored): $1" >&2 + shift + ;; + + *) # no more options. Stop while loop + break + ;; + esac +done + +inputdir=$1 +outputdir=$2 + +if [ ! -n "$inputdir" ] ; then +echo ERROR: the input directory should be specified +exit 1 +fi + +if [ ! -d "$inputdir" ] ; then +echo ERROR: the input directory does not exist +exit 1 +fi + +if [ ! -r "$inputdir" ] ; then +echo ERROR: cannot read from the input directory +exit 1 +fi + +if [ ! -n "$outputdir" ] ; then +echo ERROR: the output directory should be specified +exit 1 +fi + +if [ ! -d "$outputdir" ] ; then +mkdir -p $outputdir || ( echo ERROR: the output directory does not exist ; exit 1 ) +fi + +if [ ! -w "$outputdir" ] ; then +echo ERROR: cannot write to the output directory +exit 1 +fi + +subject=`basename $2` +experiment=`basename $1` + +echo "inputdir =" $inputdir +echo "outputdir =" $outputdir +echo "subject =" $subject +echo "experiment =" $experiment +echo "raw =" $raw +echo "anatomy =" $anatomy +echo "eprime =" $eprime +echo "datacheck =" $datacheck +echo "baddata =" $baddata +echo "icaclass =" $icaclass +echo "rmegpreproc =" $rmegpreproc +echo "tmegpreproc =" $tmegpreproc +echo "eravg =" $eravg +echo "tfavg =" $tfavg +echo "powavg =" $powavg +echo "noise =" $noise +echo "restin =" $restin +echo "wrkmem =" $wrkmem +echo "storym =" $storym +echo "motort =" $motort + +################################################################################################### +# create all possible output directories +# once all data files have been copied, all directories that are still empty will be removed +echo creating directories ... + +mkdir -p $outputdir/release-notes + +# this relies on the convention of specific experiments mapping onto scan numbers +name[1]='Rnoise' +name[2]='Pnoise' +name[3]='Restin' +name[4]='Restin' +name[5]='Restin' +name[6]='Wrkmem' +name[7]='Wrkmem' +name[8]='StoryM' +name[9]='StoryM' +name[10]='Motort' +name[11]='Motort' + +for number in 1 2 3 4 5 6 7 8 9 10 11 ; do +mkdir -p $outputdir/unprocessed/MEG/${number}-${name[$number]}/4D/ +mkdir -p $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +done + +mkdir -p $outputdir/MEG/anatomy/provenance +mkdir -p $outputdir/MEG/anatomy/figures/provenance + +for scan in Rnoise Pnoise Restin Wrkmem StoryM Motort; do +for pipeline in datacheck baddata icaclass rmegpreproc tmegpreproc eravg tfavg powavg ; do +mkdir -p $outputdir/MEG/$scan/$pipeline/provenance +mkdir -p $outputdir/MEG/$scan/$pipeline/figures/provenance +done +done + +################################################################################################### +echo writing release-notes ... + +rm -f $outputdir/release-notes/MEG.txt + +date >> $outputdir/release-notes/MEG.txt +cat << EOF >> $outputdir/release-notes/MEG.txt + +The following data processing pipelines were executed with megconnectome v1.0 +-datacheck +-anatomy +-baddata +-icaclass +-rmegpreproc +-powavg +-tmegpreproc +-tfavg +-eravg + +EOF + +cat /HCP/scratch/meg/release-notes/${experiment} >> $outputdir/release-notes/MEG.txt +cat << EOF >> $outputdir/release-notes/MEG.txt + +These data were generated and made available by the Human Connectome Project, WU-Minn Consortium (Principal Investigators: David Van Essen and Kamil Ugurbil; 1U54MH091657), which is funded by the 16 NIH Institutes and Centers that support the NIH Blueprint for Neuroscience Research and by the McDonnell Center for Systems Neuroscience at Washington University. + +For additional information on how to acknowledge HCP and cite HCP publications if you have used data provided by the WU-Minn HCP consortium, see http://www.humanconnectome.org/citations. + +As a reminder, users of these datasets must comply with the Data Use Terms that were agreed upon when receiving these data. + +EOF + +################################################################################################### +echo processing raw data ... + +if [ $raw = true ] ; then + +if [ $noise = true ] ; then +for number in 1 2 ; do +for file in config c,rfDC e,rfhp1.0Hz,COH e,rfhp1.0Hz,COH1; do +copy $inputdir/SCANS/${number}/4D/"$file" $outputdir/unprocessed/MEG/${number}-${name[$number]}/4D/ +done +done +fi + +if [ $restin = true ] ; then +for number in 3 4 5 ; do +for file in config c,rfDC e,rfhp1.0Hz,COH e,rfhp1.0Hz,COH1; do +copy $inputdir/SCANS/${number}/4D/"$file" $outputdir/unprocessed/MEG/${number}-${name[$number]}/4D/ +done +done +fi + +if [ $wrkmem = true ] ; then +for number in 6 7 ; do +for file in config c,rfDC e,rfhp1.0Hz,COH e,rfhp1.0Hz,COH1; do +copy $inputdir/SCANS/${number}/4D/"$file" $outputdir/unprocessed/MEG/${number}-${name[$number]}/4D/ +done +if [ $eprime = true ] ; then +copy $inputdir/SCANS/${number}/LINKED_DATA/EPRIME/*.xlsx $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +copy $inputdir/SCANS/${number}/LINKED_DATA/EPRIME/*.tab $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +fi +done +fi + +if [ $storym = true ] ; then +for number in 8 9 ; do +for file in config c,rfDC e,rfhp1.0Hz,COH e,rfhp1.0Hz,COH1; do +copy $inputdir/SCANS/${number}/4D/"$file" $outputdir/unprocessed/MEG/${number}-${name[$number]}/4D/ +done +if [ $eprime = true ] ; then +copy $inputdir/SCANS/${number}/LINKED_DATA/EPRIME/*.xlsx $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +copy $inputdir/SCANS/${number}/LINKED_DATA/EPRIME/*.tab $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +fi +done +fi + +if [ $motort = true ] ; then +for number in 10 11 ; do +for file in config c,rfDC e,rfhp1.0Hz,COH e,rfhp1.0Hz,COH1; do +copy $inputdir/SCANS/${number}/4D/"$file" $outputdir/unprocessed/MEG/${number}-${name[$number]}/4D/ +done +if [ $eprime = true ] ; then +copy $inputdir/SCANS/${number}/LINKED_DATA/EPRIME/*.xlsx $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +copy $inputdir/SCANS/${number}/LINKED_DATA/EPRIME/*.tab $outputdir/unprocessed/MEG/${number}-${name[$number]}/EPRIME/ +fi +done +fi + +fi + +################################################################################################### +echo processing anatomy ... + +if [ $anatomy = true ] ; then + +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_transform.txt $outputdir/MEG/anatomy +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_fiducials.txt $outputdir/MEG/anatomy +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_landmarks.txt $outputdir/MEG/anatomy +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_headmodel.mat $outputdir/MEG/anatomy +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d4mm.mat $outputdir/MEG/anatomy +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d6mm.mat $outputdir/MEG/anatomy +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d8mm.mat $outputdir/MEG/anatomy + +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_transform.txt.xml $outputdir/MEG/anatomy/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_fiducials.txt.xml $outputdir/MEG/anatomy/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_landmarks.txt.xml $outputdir/MEG/anatomy/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_headmodel.mat.xml $outputdir/MEG/anatomy/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d4mm.mat.xml $outputdir/MEG/anatomy/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d6mm.mat.xml $outputdir/MEG/anatomy/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d8mm.mat.xml $outputdir/MEG/anatomy/provenance + +for ext in png ; do + +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_headmodel.$ext $outputdir/MEG/anatomy/figures +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d4mm.$ext $outputdir/MEG/anatomy/figures +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d6mm.$ext $outputdir/MEG/anatomy/figures +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d8mm.$ext $outputdir/MEG/anatomy/figures + +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_headmodel.$ext.xml $outputdir/MEG/anatomy/figures/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d4mm.$ext.xml $outputdir/MEG/anatomy/figures/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d6mm.$ext.xml $outputdir/MEG/anatomy/figures/provenance +copy $inputdir/RESOURCES/anatomy/${subject}_MEG_anatomy_sourcemodel_3d8mm.$ext.xml $outputdir/MEG/anatomy/figures/provenance + +done + +fi + +################################################################################################### + +pipeline=() +if [ $datacheck = true ]; then pipeline=("${pipeline[@]}" datacheck ) ; fi +if [ $baddata = true ]; then pipeline=("${pipeline[@]}" baddata ) ; fi +if [ $icaclass = true ]; then pipeline=("${pipeline[@]}" icaclass ) ; fi +if [ $rmegpreproc = true ]; then pipeline=("${pipeline[@]}" rmegpreproc) ; fi +if [ $tmegpreproc = true ]; then pipeline=("${pipeline[@]}" tmegpreproc) ; fi +if [ $eravg = true ]; then pipeline=("${pipeline[@]}" eravg ) ; fi +if [ $tfavg = true ]; then pipeline=("${pipeline[@]}" tfavg ) ; fi +if [ $powavg = true ]; then pipeline=("${pipeline[@]}" powavg ) ; fi + +scan=() +if [ $noise = true ]; then scan=("${scan[@]}" Rnoise Pnoise) ; fi +if [ $restin = true ]; then scan=("${scan[@]}" Restin ) ; fi +if [ $wrkmem = true ]; then scan=("${scan[@]}" Wrkmem ) ; fi +if [ $storym = true ]; then scan=("${scan[@]}" StoryM ) ; fi +if [ $motort = true ]; then scan=("${scan[@]}" Motort ) ; fi + +echo processing processed data for ${pipeline[*]} ${scan[*]} ... + +for s in "${scan[@]}" ; do +for p in "${pipeline[@]}" ; do + +for ext in txt mat ; do +for file in $inputdir/RESOURCES/analysis/*"$s"_"$p"*."$ext" ; do +copy "$file" $outputdir/MEG/$s/$p/ +done +for file in $inputdir/RESOURCES/analysis/*"$s"_"$p"*."$ext".xml ; do +copy "$file" $outputdir/MEG/$s/$p/provenance +done +done + +for ext in png ; do +for file in $inputdir/RESOURCES/analysis/*"$s"_"$p"*."$ext" ; do +copy "$file" $outputdir/MEG/$s/$p/figures/ +done +for file in $inputdir/RESOURCES/analysis/*"$s"_"$p"*."$ext".xml ; do +copy "$file" $outputdir/MEG/$s/$p/figures/provenance/ +done +done + +done +done + +################################################################################################### +echo cleaning up empty directories ... + +find $outputdir -depth -type d -exec rmdir --ignore-fail-on-non-empty {} \; + diff --git a/bin/hcp_powavg.sh b/bin/hcp_powavg.sh new file mode 100755 index 0000000..e785254 --- /dev/null +++ b/bin/hcp_powavg.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_powavg + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_rmegpreproc.sh b/bin/hcp_rmegpreproc.sh new file mode 100755 index 0000000..70fef2d --- /dev/null +++ b/bin/hcp_rmegpreproc.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_rmegpreproc + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_tfavg.sh b/bin/hcp_tfavg.sh new file mode 100755 index 0000000..53032c9 --- /dev/null +++ b/bin/hcp_tfavg.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_tfavg + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/hcp_tmegpreproc.sh b/bin/hcp_tmegpreproc.sh new file mode 100755 index 0000000..84970a6 --- /dev/null +++ b/bin/hcp_tmegpreproc.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# This bash script allows you to run the analysis pipeline using the +# compiled megconnectome application. +# +# The distribution of the jobs is not performed in this script, but is +# handled in the MATLAB script, which can use the PBS_ARRAYID environment +# variable to select the subject and/or scan. You can run it like this +# +# qsub -l walltime=4:00:00,mem=8gb,vmem=8gb -t 1-100 +# +# This executes the present script in parallel, each instance having a different +# value of PBS_ARRAYID. + +#------------------------------ +# CODE THAT FINDS AVAILABLE Xvfb PORT AND STARTS THE VIRTUAL BUFFER +xPort=0; +tmpPort=0; +Xfiles=/tmp/.X*-lock +doneFlag=0; +while [ $doneFlag -ne 1 ] +do + for f in $Xfiles + do + tmpPort=$(echo "$f" | awk -F"X" '{print $2}' | awk -F"-" '{print $1}'); + if [[ "$tmpPort" == "$xPort" ]] + then + xPort=$((xPort+1)); + doneFlag=0; + break; + else + doneFlag=1; + fi + done +done +Xvfb :$xPort -screen 0 2760x1960x16 & +export curPID=$! +export DISPLAY=:$xPort +#------------------------------ + +# process the command line arguments, keep spaces intact +shift 0 +args= +while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift +done + +if [ -d /export/matlab/MCR/R2012b/v80 ]; then +# this applies to the CHPC cluster in St Louis +MCRROOT=/export/matlab/MCR/R2012b/v80 +fi + +if [ -d /opt/MCR/R2012b/v80 ]; then +# this applies to the Nijmegen cluster +MCRROOT=/opt/MCR/R2012b/v80 +fi + +# determine where the shell and MATLAB scripts are located +MEGCONNECTOME_ROOT=$HOME/matlab/megconnectome +MEGCONNECTOME_BIN=$MEGCONNECTOME_ROOT/bin +MEGCONNECTOME_SCRIPT=$MEGCONNECTOME_ROOT/pipeline_scripts + +# determine the name of the pipeline script that should run +PIPELINE=hcp_tmegpreproc + +$MEGCONNECTOME_BIN/megconnectome.sh $MCRROOT $MEGCONNECTOME_SCRIPT/$PIPELINE.m $args + +#------------------------------ +#CLOSE VIRTUAL BUFFER +kill $curPID + diff --git a/bin/megconnectome b/bin/megconnectome new file mode 100755 index 0000000..7bf69cd Binary files /dev/null and b/bin/megconnectome differ diff --git a/bin/megconnectome.sh b/bin/megconnectome.sh new file mode 100755 index 0000000..d17e229 --- /dev/null +++ b/bin/megconnectome.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# script for execution of deployed applications +# +# Sets up the MCR environment for the current $ARCH and executes +# the specified command. +# +exe_name=$0 +exe_dir=`dirname "$0"` +if [ "x$1" = "x" ]; then + echo "------------------------------------------" + echo Usage: + echo $0 \ args + echo "------------------------------------------" +else + # echo Setting up environment variables + MCRROOT="$1" + # echo --- + LD_LIBRARY_PATH=.:${MCRROOT}/runtime/glnxa64 ; + LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${MCRROOT}/bin/glnxa64 ; + LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${MCRROOT}/sys/os/glnxa64; + MCRJRE=${MCRROOT}/sys/java/jre/glnxa64/jre/lib/amd64 ; + LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${MCRJRE}/native_threads ; + LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${MCRJRE}/server ; + LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${MCRJRE}/client ; + LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${MCRJRE} ; + XAPPLRESDIR=${MCRROOT}/X11/app-defaults ; + export LD_LIBRARY_PATH; + export XAPPLRESDIR; + # echo LD_LIBRARY_PATH is ${LD_LIBRARY_PATH}; + shift 1 + args= + while [ $# -gt 0 ]; do + token=`echo "$1" | sed 's/ /\\\\ /g'` # Add blackslash before each blank + args="${args} ${token}" + shift + done + eval "${exe_dir}"/megconnectome $args +fi +exit + diff --git a/build/compile_megconnectome.m b/build/compile_megconnectome.m new file mode 100644 index 0000000..b9ca66e --- /dev/null +++ b/build/compile_megconnectome.m @@ -0,0 +1,162 @@ +function compile_megconnectome(fieldtriproot, hcproot) + +% COMPILE_MEGCONNECTOME compiles the hcp and fieldtrip functions along with +% the "megconnectome" entry function into a stand-alone compiled executable. +% +% The compiled executable includes +% - all main fieldtrip m-files +% - all main fieldtrip's m-files dependencies for as long as these +% dependencies are in the fieldtrip modules on the path, matlab +% built-in, or toolbox/(stats/images/signal) functions +% - the megconnectome/analysis_functions and its subdirectories +% +% See also MEGCONNECTOME + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% clear all variables, globals, functions and MEX links +clear all + +fname = 'megconnectome'; + +v = ver('MATLAB'); +if ~strcmp(v.Version, '8.0') + error('the megconnectome application should be compiled with MATLAB 2012b (8.0)'); +end + +if nargin<1 || isempty(fieldtriproot) + fieldtriproot = fileparts(which('ft_defaults')); +end + +if nargin<2 || isempty(hcproot) + % this script is in hcproot/build + hcproot = fileparts(which(mfilename)); + hcproot = fileparts(hcproot); +end + +origdir = pwd; +builddir = fullfile(hcproot, 'build'); +bindir = fullfile(hcproot, 'bin'); + +cd(builddir); + +% create a file with the timestamp of the compilation +fid = fopen('buildtimestamp.m', 'wt'); +fprintf(fid, 'function s = buildtimestamp\n'); +fprintf(fid, 's = ''%s'';\n', datestr(now)); +fclose(fid); + +cd(bindir); + +fprintf('Using fieldtrip from "%s"\n', fieldtriproot); +fprintf('Using megconnectome from "%s"\n', hcproot); + +% clean the path +restoredefaultpath; + +%-------------------------------- +% FIELDTRIP RELATED PATH SETTINGS + +% add the path to fieldtrip +addpath(fieldtriproot); + +% ensure that the path to the default modules is specified +clear ft_defaults; +ft_defaults; + +% do not use my personal defaults, but rather FieldTrip standard defaults +global ft_default +ft_default = []; + +% do not use my personal defaults, but rather HCP standard defaults +global hcp_default +hcp_default = []; + +% ensure that these special modules are also added +ft_hastoolbox('qsub', 1); +ft_hastoolbox('engine', 1); + +% ensure that all external toolboxes are added to the path +% excluding spm2 (leading to more than one spm on the path -> confusion in FT) +exclude = { + '.' + '..' + '.svn' + 'dipoli' + 'dmlt' + 'iso2mesh' + 'simbio' + 'spm2' + 'sqdproject' + 'yokogawa' + }; +extd = dir([fieldtriproot,'/external']); +extd = setdiff({extd([extd.isdir]).name}, exclude); +for k = 1:numel(extd) + addpath(fullfile(fieldtriproot,'external',extd{k})); +end + +%-------------------------- +% HCP RELATED PATH SETTINGS + +addpath(hcproot); +addpath(fullfile(hcproot, 'external')); +addpath(fullfile(hcproot, 'analysis_functions')); +addpath(fullfile(hcproot, 'trial_functions')); + +%------------------- +% DO THE COMPILATION + +cmd = ['mcc -R -singleCompThread -N -o ' fname ' -m ' fname ... + ' -a ' fieldtriproot '/*.m' ... + ' -a ' fieldtriproot '/utilities/*.m' ... + ' -a ' fieldtriproot '/fileio/*.m' ... + ' -a ' fieldtriproot '/forward/*.m' ... + ' -a ' fieldtriproot '/inverse/*.m' ... + ' -a ' fieldtriproot '/plotting/*.m' ... + ' -a ' fieldtriproot '/statfun/*.m' ... + ' -a ' fieldtriproot '/trialfun/*.m' ... + ' -a ' fieldtriproot '/preproc/*.m' ... + ' -a ' fieldtriproot '/qsub/*.m' ... + ' -a ' fieldtriproot '/engine/*.m' ... + ' -a ' fieldtriproot '/external/plot2svg/*.m' ... + ' -a ' hcproot '/Contents.m' ... + ' -a ' hcproot '/build/buildtimestamp.m' ... + ' -a ' hcproot '/build/megconnectome.m' ... + ' -a ' hcproot '/external/*.m' ... + ' -a ' hcproot '/external/*.mex*' ... + ' -a ' hcproot '/analysis_functions/*.m' ... + ' -a ' hcproot '/trial_functions/*.m' ... + ' -p ' matlabroot '/toolbox/signal' ... + ' -p ' matlabroot '/toolbox/images' ... + ' -p ' matlabroot '/toolbox/stats' ... + ' -p ' matlabroot '/toolbox/optim' ... + ' -p ' matlabroot '/toolbox/curvefit' ... + ]; +eval(cmd); + +% somehow I don't manage to get this going with more than one directory to be added when calling mcc directly: +% mcc('-N', '-a', '/home/common/matlab/fieldtrip/*.m', '-o', fname, '-m', fname, '-p', [matlabroot,'/toolbox/signal:', matlabroot,'/toolbox/images:', matlabroot,'/toolbox/stats']); + +% remove the additional files that were created during compilation +delete mccExcludedFiles.log +delete readme.txt +delete run_megconnectome.sh + +fprintf('Finished compilation\n'); +cd(origdir); diff --git a/build/megconnectome.m b/build/megconnectome.m new file mode 100644 index 0000000..f1f266b --- /dev/null +++ b/build/megconnectome.m @@ -0,0 +1,200 @@ +function megconnectome(varargin) + +% MEGCONNECTOME is the entry function of the compiled "megconnectome.exe" +% application that includes fieldtrip and the megconnectome/analysis_functions. +% The compiled application can be used to execute the scripts that are found +% in megconnectome/analysis_scripts. +% +% This function can be started on the MATLAB command line as +% megconnectome scriptname.m +% megconnectome script1.m script2.m ... +% megconnectome jobfile.mat +% or after compilation on the Linux command line as +% megconnectome.sh +% megconnectome.sh scriptname.m +% megconnectome.sh script1.m script2.m ... +% megconnectome.sh jobfile.mat +% +% It is possible to pass additional options on the MATLAB command line like +% this on the MATLAB command line +% megconnectome --option value scriptname.m +% or on the Linux command line +% megconnectome.sh --option value scriptname.m +% The options and their values are automaticallly made available as local +% variables in the script execution environment. +% +% More information is available on +% https://wiki.humanconnectome.org/display/FieldTrip/FieldTrip +% +% See also COMPILE_MEGCONNECTOME + +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +% this function uses assignin/evalin. The alternative is to use assign/eval +% but then the script execution might collide with local variables inside this +% megconnectome function +workspace = 'caller'; + +% separate the --options from the filenames +[options, varargin] = hcp_getopt(varargin{:}); + +if any(strcmp(options(1:2:end), 'version')) + % only show the provenance information, do not run anything + varargin = {}; +end + +% get the general provenance information (e.g. user and hostname) +prov = hcp_provenance; + +for i=1:length(varargin) + fname = varargin{i}; + + % get the specific provenance information for the script + prov.script{i}.filename = fname; + prov.script{i}.md5sum = hcp_md5sum(fname); + + % The following section with "which" does not work in the compiled application + % + % if isempty(which(fname)) + % error('The file %s cannot be found\n',fname); + % end + % % ensure that the script name includes the full path and extension + % fname = which(fname); + + [p, f, ext] = fileparts(fname); + + switch ext + case '.m' + if ~exist(fname, 'file') + error('The script %s cannot be found\n',fname); + end + + fid = fopen(fname); + if fid == -1, error('Cannot open %s',fname); end + S = fscanf(fid,'%c'); + fclose(fid); + + prov.script{i}.content = sprintf('\n%s\n', S); + + % capture all screen output in a diary file + diary off + diaryfile = tempname; + diary(diaryfile); + + try + % ensure that subsequent scripts do not interfere with each other + % keep the hcp_default global variable + global hcp_default + localcopy = hcp_default; + evalin(workspace,'clear all'); + global hcp_default + hcp_default = localcopy; + + % make the options available as local variables + for j=1:2:length(options) + key = options{j}; + val = options{j+1}; + + if ismember(val(1), {'[', '{'}) + % the value is a string that represents an array + evalin(workspace, sprintf('%s = %s;', key, val)); + elseif ismember(val(1), {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '+'}) && all(ismember(cellstr(val(:)), {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '+', '.', 'e'})) + % the value is a string that represents a number + evalin(workspace, sprintf('%s = %s;', key, val)); + else + assignin(workspace, key, val); + end + end + + % add some information about the script to the global hcp_default variable + % this will be picked up by hcp_write_provenance + hcp_default.prov.script = prov.script{i}; + + % evaluate this script + evalin(workspace,S); + + % remove the provenance information about the script + hcp_default.prov.script = []; + + catch err + % remove the provenance information about the script + hcp_default.prov.script = []; + + fprintf('Execution failed: %s\n', fname); + rethrow(err); + end + + case '.mat' + % The job input consists of a file like this + % /Users/robert/tmp/robert_mbp_p36978_b1_j001_input.mat + % which after execution results in an output file + % /Users/robert/tmp/robert_mbp_p36978_b1_j001_output.mat + + if length(fname)<10 + error('The jobfile %s is incorrect\n',fname); + elseif ~strcmp(fname(end-9:end), '_input.mat') + error('The jobfile %s is incorrect\n',fname); + end + + jobid = fname(1:end-10); + [p, jobid] = fileparts(jobid); + cd(p); + + % read the job input arguments from jobid_input.mat, delete the input file and execute the job + qsubexec(jobid); + + if isempty(getenv('PBS_ENVIRONMENT')) + % this suggests that the job is running outside of the torque environment, e.g. for local testing + % execution of qsubexec is expected to result in a stdout and stderr log file from torque + logout = fullfile(p, sprintf('%s.o', jobid)); + logerr = fullfile(p, sprintf('%s.e', jobid)); + fclose(fopen(logout, 'w')); + fclose(fopen(logerr, 'w')); + end + + % read the job results from jobid_output.mat, delete the output file + % FIXME what to do with the output variable? + argout = qsubget(jobid); + + otherwise + error('Unknown input format for %s, should be *.m or *.mat\n',fname); + end % switch type of input argument + + % force close all figures that were created + close all + + diary off + fid = fopen(diaryfile); + if fid == -1, error('Cannot open %s',diaryfile); end + S = fscanf(fid,'%c'); + fclose(fid); + delete(diaryfile); + + % add the screen output to the provenance structure + prov.script{i}.stdout = sprintf('\n%s\n', S); + +end % for each of the input arguments + +% display the provenance structure on screen as an XML stream +disp(fileexchange_struct2xml(struct('megconnectome', prov))); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function assign(key, val) +assignin('caller', key, val); diff --git a/external/fileexchange_struct2xml.LICENSE b/external/fileexchange_struct2xml.LICENSE new file mode 100644 index 0000000..21b570d --- /dev/null +++ b/external/fileexchange_struct2xml.LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2010, Wouter Falkena +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/external/fileexchange_struct2xml.m b/external/fileexchange_struct2xml.m new file mode 100644 index 0000000..acdbc6a --- /dev/null +++ b/external/fileexchange_struct2xml.m @@ -0,0 +1,198 @@ +function varargout = struct2xml( s, varargin ) +%Convert a MATLAB structure into a xml file +% [ ] = struct2xml( s, file ) +% xml = struct2xml( s ) +% +% A structure containing: +% s.XMLname.Attributes.attrib1 = "Some value"; +% s.XMLname.Element.Text = "Some text"; +% s.XMLname.DifferentElement{1}.Attributes.attrib2 = "2"; +% s.XMLname.DifferentElement{1}.Text = "Some more text"; +% s.XMLname.DifferentElement{2}.Attributes.attrib3 = "2"; +% s.XMLname.DifferentElement{2}.Attributes.attrib4 = "1"; +% s.XMLname.DifferentElement{2}.Text = "Even more text"; +% +% Will produce: +% +% Some text +% Some more text +% Even more text +% +% +% Please note that the following strings are substituted +% '_dash_' by '-', '_colon_' by ':' and '_dot_' by '.' +% +% Written by W. Falkena, ASTI, TUDelft, 27-08-2010 +% On-screen output functionality added by P. Orth, 01-12-2010 +% Multiple space to single space conversion adapted for speed by T. Lohuis, 11-04-2011 +% Val2str subfunction bugfix by H. Gsenger, 19-9-2011 + + if (nargin ~= 2) + if(nargout ~= 1 || nargin ~= 1) + error(['Supported function calls:' sprintf('\n')... + '[ ] = struct2xml( s, file )' sprintf('\n')... + 'xml = struct2xml( s )']); + end + end + + if(nargin == 2) + file = varargin{1}; + + if (isempty(file)) + error('Filename can not be empty'); + end + + if (isempty(strfind(file,'.xml'))) + file = [file '.xml']; + end + end + + if (~isstruct(s)) + error([inputname(1) ' is not a structure']); + end + + if (length(fieldnames(s)) > 1) + error(['Error processing the structure:' sprintf('\n') 'There should be a single field in the main structure.']); + end + xmlname = fieldnames(s); + xmlname = xmlname{1}; + + %substitute special characters + xmlname_sc = xmlname; + xmlname_sc = strrep(xmlname_sc,'_dash_','-'); + xmlname_sc = strrep(xmlname_sc,'_colon_',':'); + xmlname_sc = strrep(xmlname_sc,'_dot_','.'); + + %create xml structure + docNode = com.mathworks.xml.XMLUtils.createDocument(xmlname_sc); + + %process the rootnode + docRootNode = docNode.getDocumentElement; + + %append childs + parseStruct(s.(xmlname),docNode,docRootNode,[inputname(1) '.' xmlname '.']); + + if(nargout == 0) + %save xml file + xmlwrite(file,docNode); + else + varargout{1} = xmlwrite(docNode); + end +end + +% ----- Subfunction parseStruct ----- +function [] = parseStruct(s,docNode,curNode,pName) + + fnames = fieldnames(s); + for i = 1:length(fnames) + curfield = fnames{i}; + + %substitute special characters + curfield_sc = curfield; + curfield_sc = strrep(curfield_sc,'_dash_','-'); + curfield_sc = strrep(curfield_sc,'_colon_',':'); + curfield_sc = strrep(curfield_sc,'_dot_','.'); + + if (strcmp(curfield,'Attributes')) + %Attribute data + if (isstruct(s.(curfield))) + attr_names = fieldnames(s.Attributes); + for a = 1:length(attr_names) + cur_attr = attr_names{a}; + + %substitute special characters + cur_attr_sc = cur_attr; + cur_attr_sc = strrep(cur_attr_sc,'_dash_','-'); + cur_attr_sc = strrep(cur_attr_sc,'_colon_',':'); + cur_attr_sc = strrep(cur_attr_sc,'_dot_','.'); + + [cur_str,succes] = val2str(s.Attributes.(cur_attr)); + if (succes) + curNode.setAttribute(cur_attr_sc,cur_str); + else + disp(['Warning. The text in ' pName curfield '.' cur_attr ' could not be processed.']); + end + end + else + disp(['Warning. The attributes in ' pName curfield ' could not be processed.']); + disp(['The correct syntax is: ' pName curfield '.attribute_name = ''Some text''.']); + end + elseif (strcmp(curfield,'Text')) + %Text data + [txt,succes] = val2str(s.Text); + if (succes) + curNode.appendChild(docNode.createTextNode(txt)); + else + disp(['Warning. The text in ' pName curfield ' could not be processed.']); + end + else + %Sub-element + if (isstruct(s.(curfield))) + %single element + curElement = docNode.createElement(curfield_sc); + curNode.appendChild(curElement); + parseStruct(s.(curfield),docNode,curElement,[pName curfield '.']) + elseif (iscell(s.(curfield))) + %multiple elements + for c = 1:length(s.(curfield)) + curElement = docNode.createElement(curfield_sc); + curNode.appendChild(curElement); + if (isstruct(s.(curfield){c})) + parseStruct(s.(curfield){c},docNode,curElement,[pName curfield '{' num2str(c) '}.']) + else + disp(['Warning. The cell ' pName curfield '{' num2str(c) '} could not be processed, since it contains no structure.']); + end + end + else + %eventhough the fieldname is not text, the field could + %contain text. Create a new element and use this text + curElement = docNode.createElement(curfield_sc); + curNode.appendChild(curElement); + [txt,succes] = val2str(s.(curfield)); + if (succes) + curElement.appendChild(docNode.createTextNode(txt)); + else + disp(['Warning. The text in ' pName curfield ' could not be processed.']); + end + end + end + end +end + +%----- Subfunction val2str ----- +function [str,succes] = val2str(val) + + succes = true; + str = []; + + if (isempty(val)) + return; %bugfix from H. Gsenger + elseif (ischar(val)) + %do nothing + elseif (isnumeric(val)) + val = num2str(val); + else + succes = false; + end + + if (ischar(val)) + %add line breaks to all lines except the last (for multiline strings) + lines = size(val,1); + val = [val char(sprintf('\n')*[ones(lines-1,1);0])]; + + %transpose is required since indexing (i.e., val(nonspace) or val(:)) produces a 1-D vector. + %This should be row based (line based) and not column based. + valt = val'; + + remove_multiple_white_spaces = true; + if (remove_multiple_white_spaces) + %remove multiple white spaces using isspace, suggestion of T. Lohuis + whitespace = isspace(val); + nonspace = (whitespace + [zeros(lines,1) whitespace(:,1:end-1)])~=2; + nonspace(:,end) = [ones(lines-1,1);0]; %make sure line breaks stay intact + str = valt(nonspace'); + else + str = valt(:); + end + end +end diff --git a/external/fileexchange_xml2struct.LICENSE b/external/fileexchange_xml2struct.LICENSE new file mode 100644 index 0000000..21b570d --- /dev/null +++ b/external/fileexchange_xml2struct.LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2010, Wouter Falkena +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/external/fileexchange_xml2struct.m b/external/fileexchange_xml2struct.m new file mode 100644 index 0000000..bd0412d --- /dev/null +++ b/external/fileexchange_xml2struct.m @@ -0,0 +1,183 @@ +function [ s ] = xml2struct( file ) +%Convert xml file into a MATLAB structure +% [ s ] = xml2struct( file ) +% +% A file containing: +% +% Some text +% Some more text +% Even more text +% +% +% Will produce: +% s.XMLname.Attributes.attrib1 = "Some value"; +% s.XMLname.Element.Text = "Some text"; +% s.XMLname.DifferentElement{1}.Attributes.attrib2 = "2"; +% s.XMLname.DifferentElement{1}.Text = "Some more text"; +% s.XMLname.DifferentElement{2}.Attributes.attrib3 = "2"; +% s.XMLname.DifferentElement{2}.Attributes.attrib4 = "1"; +% s.XMLname.DifferentElement{2}.Text = "Even more text"; +% +% Please note that the following characters are substituted +% '-' by '_dash_', ':' by '_colon_' and '.' by '_dot_' +% +% Written by W. Falkena, ASTI, TUDelft, 21-08-2010 +% Attribute parsing speed increased by 40% by A. Wanner, 14-6-2011 +% Added CDATA support by I. Smirnov, 20-3-2012 +% +% Modified by X. Mo, University of Wisconsin, 12-5-2012 + + if (nargin < 1) + clc; + help xml2struct + return + end + + if isa(file, 'org.apache.xerces.dom.DeferredDocumentImpl') || isa(file, 'org.apache.xerces.dom.DeferredElementImpl') + % input is a java xml object + xDoc = file; + else + %check for existance + if (exist(file,'file') == 0) + %Perhaps the xml extension was omitted from the file name. Add the + %extension and try again. + if (isempty(strfind(file,'.xml'))) + file = [file '.xml']; + end + + if (exist(file,'file') == 0) + error(['The file ' file ' could not be found']); + end + end + %read the xml file + xDoc = xmlread(file); + end + + %parse xDoc into a MATLAB structure + s = parseChildNodes(xDoc); + +end + +% ----- Subfunction parseChildNodes ----- +function [children,ptext,textflag] = parseChildNodes(theNode) + % Recurse over node children. + children = struct; + ptext = struct; textflag = 'Text'; + if hasChildNodes(theNode) + childNodes = getChildNodes(theNode); + numChildNodes = getLength(childNodes); + + for count = 1:numChildNodes + theChild = item(childNodes,count-1); + [text,name,attr,childs,textflag] = getNodeData(theChild); + + if (~strcmp(name,'#text') && ~strcmp(name,'#comment') && ~strcmp(name,'#cdata_dash_section')) + %XML allows the same elements to be defined multiple times, + %put each in a different cell + if (isfield(children,name)) + if (~iscell(children.(name))) + %put existsing element into cell format + children.(name) = {children.(name)}; + end + index = length(children.(name))+1; + %add new element + children.(name){index} = childs; + if(~isempty(fieldnames(text))) + children.(name){index} = text; + end + if(~isempty(attr)) + children.(name){index}.('Attributes') = attr; + end + else + %add previously unknown (new) element to the structure + children.(name) = childs; + if(~isempty(text) && ~isempty(fieldnames(text))) + children.(name) = text; + end + if(~isempty(attr)) + children.(name).('Attributes') = attr; + end + end + else + ptextflag = 'Text'; + if (strcmp(name, '#cdata_dash_section')) + ptextflag = 'CDATA'; + elseif (strcmp(name, '#comment')) + ptextflag = 'Comment'; + end + + %this is the text in an element (i.e., the parentNode) + if (~isempty(regexprep(text.(textflag),'[\s]*',''))) + if (~isfield(ptext,ptextflag) || isempty(ptext.(ptextflag))) + ptext.(ptextflag) = text.(textflag); + else + %what to do when element data is as follows: + %Text More text + + %put the text in different cells: + % if (~iscell(ptext)) ptext = {ptext}; end + % ptext{length(ptext)+1} = text; + + %just append the text + ptext.(ptextflag) = [ptext.(ptextflag) text.(textflag)]; + end + end + end + + end + end +end + +% ----- Subfunction getNodeData ----- +function [text,name,attr,childs,textflag] = getNodeData(theNode) + % Create structure of node info. + + %make sure name is allowed as structure name + name = toCharArray(getNodeName(theNode))'; + name = strrep(name, '-', '_dash_'); + name = strrep(name, ':', '_colon_'); + name = strrep(name, '.', '_dot_'); + + attr = parseAttributes(theNode); + if (isempty(fieldnames(attr))) + attr = []; + end + + %parse child nodes + [childs,text,textflag] = parseChildNodes(theNode); + + if (isempty(fieldnames(childs)) && isempty(fieldnames(text))) + %get the data of any childless nodes + % faster than if any(strcmp(methods(theNode), 'getData')) + % no need to try-catch (?) + % faster than text = char(getData(theNode)); + text.(textflag) = toCharArray(getTextContent(theNode))'; + end + +end + +% ----- Subfunction parseAttributes ----- +function attributes = parseAttributes(theNode) + % Create attributes structure. + + attributes = struct; + if hasAttributes(theNode) + theAttributes = getAttributes(theNode); + numAttributes = getLength(theAttributes); + + for count = 1:numAttributes + %attrib = item(theAttributes,count-1); + %attr_name = regexprep(char(getName(attrib)),'[-:.]','_'); + %attributes.(attr_name) = char(getValue(attrib)); + + %Suggestion of Adrian Wanner + str = toCharArray(toString(item(theAttributes,count-1)))'; + k = strfind(str,'='); + attr_name = str(1:(k(1)-1)); + attr_name = strrep(attr_name, '-', '_dash_'); + attr_name = strrep(attr_name, ':', '_colon_'); + attr_name = strrep(attr_name, '.', '_dot_'); + attributes.(attr_name) = str((k(1)+2):(end-1)); + end + end +end \ No newline at end of file diff --git a/external/miniz.LICENSE b/external/miniz.LICENSE new file mode 100644 index 0000000..3a94224 --- /dev/null +++ b/external/miniz.LICENSE @@ -0,0 +1,25 @@ + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + diff --git a/external/miniz.cpp b/external/miniz.cpp new file mode 100755 index 0000000..d47eaa7 --- /dev/null +++ b/external/miniz.cpp @@ -0,0 +1,4768 @@ +/* miniz.c v1.14 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated May 20, 2012 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. + Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly + "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. + Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. + Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. + Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). + 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. + level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. + 5/28/11 v1.11 - Added statement from unlicense.org + 5/27/11 v1.10 - Substantial compressor optimizations: + Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. + Refactored the compression code for better readability and maintainability. + Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large + drop in throughput on some files). + 5/15/11 v1.09 - Initial stable release. + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or +// get/set file times. +//#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc +// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user +// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if MINIZ_X86_OR_X64_CPU +// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +#define MZ_VERSION "9.1.14" +#define MZ_VERNUM 0x91E0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 14 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s +{ + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. +// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) +// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). +// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. +// Parameters: +// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. +// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. +// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). +// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again +// with more input data, or with more room in the output buffer (except when using single call decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef mz_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH MZ_NO_FLUSH + #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FULL_FLUSH MZ_FULL_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_BLOCK MZ_BLOCK + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define Z_PARAM_ERROR MZ_PARAM_ERROR + #define Z_NO_COMPRESSION MZ_NO_COMPRESSION + #define Z_BEST_SPEED MZ_BEST_SPEED + #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY + #define Z_FILTERED MZ_FILTERED + #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY + #define Z_RLE MZ_RLE + #define Z_FIXED MZ_FIXED + #define Z_DEFLATED MZ_DEFLATED + #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS + #define alloc_func mz_alloc_func + #define free_func mz_free_func + #define internal_state mz_internal_state + #define z_stream mz_stream + #define deflateInit mz_deflateInit + #define deflateInit2 mz_deflateInit2 + #define deflateReset mz_deflateReset + #define deflate mz_deflate + #define deflateEnd mz_deflateEnd + #define deflateBound mz_deflateBound + #define compress mz_compress + #define compress2 mz_compress2 + #define compressBound mz_compressBound + #define inflateInit mz_inflateInit + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define uncompress mz_uncompress + #define crc32 mz_crc32 + #define adler32 mz_adler32 + #define MAX_WBITS 15 + #define MAX_MEM_LEVEL 9 + #define zError mz_error + #define ZLIB_VERSION MZ_VERSION + #define ZLIB_VERNUM MZ_VERNUM + #define ZLIB_VER_MAJOR MZ_VER_MAJOR + #define ZLIB_VER_MINOR MZ_VER_MINOR + #define ZLIB_VER_REVISION MZ_VER_REVISION + #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION + #define zlibVersion mz_version + #define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// Works around MSVC's spammy "warning C4127: conditional expression is constant" message. +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum +{ + MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct +{ + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum +{ + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + mz_uint m_total_files; + mz_zip_mode m_zip_mode; + + mz_uint m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum +{ + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and modified times. +// This function only extracts files, not archive directory records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. +// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. +// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). +// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. +// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before +// the archive is finalized the file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. +// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by the end of central directory record. +// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). +// An archive must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. +// Note for the archive to be valid, it must have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. +// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. +// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. +// Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. +// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): +// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). +enum +{ + TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 8, TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. +// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. +// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). +// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) +// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). +// On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. +// The caller must free() the returned block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. +// Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int max_probes, int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#else +enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; +#endif + +// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. +typedef enum +{ + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum +{ + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. +// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. +// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. +// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. +// tdefl_compress_buffer() always consumes the entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) +// window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; + +#include +#include + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC + #define MZ_MALLOC(x) NULL + #define MZ_FREE(x) (void)x, ((void)0) + #define MZ_REALLOC(p, x) NULL +#else MINIZ_NO_MALLOC + /* Redefine memory allocation routines to use MatLab's own */ + #define MZ_MALLOC(x) mxMalloc(x) + #define MZ_FREE(x) mxFree(x) + #define MZ_REALLOC(p, x) mxRealloc(p, x) +#endif + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#ifdef _MSC_VER + #define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) + #define MZ_FORCEINLINE __attribute__((__always_inline__)) +#else + #define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// ------------------- zlib-style API's + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + return (s2 << 16) + s1; +} + +// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) return MZ_CRC32_INIT; + crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } + return ~crcu32; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; + for ( ; ; ) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = def_alloc_func; + if (!pStream->zfree) pStream->zfree = def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) + { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct { int m_err; const char *m_pDesc; } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, + { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif //MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never +// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. +// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a +// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the +// bit buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read +// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully +// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. +// The slow path is only executed at the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; + static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } + + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } +#else + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); *pOut_len = 0; return NULL; + } + pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for ( ; ; ) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, + 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, + 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, + 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, + 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, + 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, + 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, + 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7 }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, + 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, + 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. +typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } + for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } + A[0].m_key += A[1].m_key; root = 0; leaf = 2; + for (next=1; next < n-1; next++) + { + if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; + avbl = 1; used = dpth = 0; root = n-2; next = n-1; + while (avbl>0) + { + while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } + while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } + avbl = 2*used; dpth++; used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; mz_uint32 total = 0; if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) do { \ + mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ +} MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ +} rle_repeat_count = 0; } } + +#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ +} rle_z_count = 0; } } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. + if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) + { + mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } + } + else + { + mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + if (!probe_len) + { + *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; + for ( ; ; ) + { + for ( ; ; ) + { + if (--num_probes_left == 0) return; + #define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; + TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; + } + if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; + if (probe_len > match_len) + { + *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && + (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output buffer. + if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) + { + int n; + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) + { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; + d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; + do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; + *pOut_len = out_buf.m_size; return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; + return out_buf.m_size; +} + +#ifndef MINIZ_NO_ZLIB_APIS +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} +#endif //MINIZ_NO_ZLIB_APIS + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int max_probes, int num_chans, size_t *pLen_out) +{ + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; + if (!pComp) return NULL; + MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } + // write dummy header + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + // max_probes used to be TDEFL_DEFAULT_MAX_PROBES: [0 4095] + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, max_probes | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + y * bpl, bpl, TDEFL_NO_FLUSH); } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + // write real header + *pLen_out = out_buf.m_size-41; + { + mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, + 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,"\0\0\04\02\06"[num_chans],0,0,0,0,0,0,0, + (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; + c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef MINIZ_NO_STDIO + #define MZ_FILE void * +#else + #include + #include + #if defined(_MSC_VER) || defined(__MINGW64__) + #include + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 _ftelli64 + #define MZ_FSEEK64 _fseeki64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #elif defined(__MINGW32__) + #include + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello64 + #define MZ_FSEEK64 fseeko64 + #define MZ_FILE_STAT_STRUCT _stat + #define MZ_FILE_STAT _stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #else + #include + #define MZ_FILE FILE + #define MZ_FOPEN fopen + #define MZ_FCLOSE fclose + #define MZ_FREAD fread + #define MZ_FWRITE fwrite + #define MZ_FTELL64 ftello + #define MZ_FSEEK64 fseeko + #define MZ_FILE_STAT_STRUCT stat + #define MZ_FILE_STAT stat + #define MZ_FFLUSH fflush + #define MZ_FREOPEN freopen + #define MZ_DELETE_FILE remove + #endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. +enum +{ + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + // Central directory header record offsets + MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + // Local directory header offsets + MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + MZ_FILE *m_pFile; + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; + if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; + pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ + struct tm *tm = localtime(&time); + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif + +#ifndef MINIZ_NO_STDIO +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef MINIZ_NO_TIME + (void)pFilename; *pDOS_date = *pDOS_time = 0; +#else + struct MZ_FILE_STAT_STRUCT file_stat; if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; + mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); +#endif // #ifdef MINIZ_NO_TIME + return MZ_TRUE; +} + +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) +{ +#ifndef MINIZ_NO_TIME + struct utimbuf t; t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); +#else + pFilename, access_time, modified_time; + return MZ_TRUE; +#endif // #ifndef MINIZ_NO_TIME +} +#endif + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END + +// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) + { + int child, root = start; + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= size) + break; + child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + start--; + } + + end = size - 1; + while (end > 0) + { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for ( ; ; ) + { + if ((child = (root << 1) + 1) >= end) + break; + child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) +{ + mz_uint cdir_size, num_this_disk, cdir_disk_index; + mz_uint64 cdir_ofs; + mz_int64 cur_file_ofs; + const mz_uint8 *p; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + // Find the end of central directory record by scanning the file from the end towards the beginning. + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for ( ; ; ) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + for (i = n - 4; i >= 0; --i) + if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + break; + if (i >= 0) + { + cur_file_ofs += i; + break; + } + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + // Read and verify the end of central directory record. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || + ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) + return MZ_FALSE; + + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return MZ_FALSE; + + if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return MZ_FALSE; + + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return MZ_FALSE; + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return MZ_FALSE; + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return MZ_FALSE; + + // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, comp_size, decomp_size, disk_index; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return MZ_FALSE; + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) + return MZ_FALSE; + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index != num_this_disk) && (disk_index != 1)) + return MZ_FALSE; + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return MZ_FALSE; + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return MZ_FALSE; + n -= total_header_size; p += total_header_size; + } + } + + if ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) +{ + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pMem = (void *)pMem; + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return MZ_FALSE; + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, internal_attr, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((!internal_attr) && ((external_attr & 0x10) != 0)) + return MZ_TRUE; + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) + { + int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint file_index; size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_p)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); if (name_len > 0xFFFF) return -1; + comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return MZ_FALSE; + return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); + } + + // Decompress the file either directly from memory or from a file input buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_uncomp_size; + } + else + { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#endif + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#endif + return NULL; + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input buffer. + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) + { +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#endif + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} +#endif + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } +static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) + { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); +#ifdef _MSC_VER + if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#else + if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#endif + return 0; + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max size + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + pFilename; return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } + else if (pState->m_pMem) + { + // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) + { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#ifndef MINIZ_NO_TIME + { + time_t cur_time; time(&cur_time); + mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + return MZ_FALSE; + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) + { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) + return MZ_FALSE; + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for ( ; ; ) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) + return MZ_FALSE; + + while (comp_bytes_remaining) + { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)pState->m_central_dir.m_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } + else + { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) + { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) + if (!mz_zip_writer_finalize_archive(&zip_archive)) + status = MZ_FALSE; + if (!mz_zip_writer_end(&zip_archive)) + status = MZ_FALSE; + if ((!status) && (created_new_archive)) + { + // It's a new archive and something went wrong, so just delete it. + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/external/savepng.LICENSE b/external/savepng.LICENSE new file mode 100644 index 0000000..ccd9245 --- /dev/null +++ b/external/savepng.LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2013, Stefan Slonevskiy +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/external/savepng.README b/external/savepng.README new file mode 100644 index 0000000..21c5567 --- /dev/null +++ b/external/savepng.README @@ -0,0 +1,43 @@ +## Overview + +savepng is a very efficient PNG image compression routine that outperforms MatLab's built-in imwrite in compression times and smaller file sizes. + +PNG encoding is based on public-domain [MINIZ library](http://code.google.com/p/miniz/). + +## Usage + +```matlab +savepng(CDATA,filename[,Compression]) +``` + +Where, + +* `CDATA` is a standard MatLab image m-by-n-by-3 matrix. This matrix can be obtained using `getframe` command or, for a faster implementation, [undocumented hardcopy command](http://www.mathworks.com/support/solutions/en/data/1-3NMHJ5/) +* `filename` file name of the image to write. Don't forget to add .png to the file name. +* `Compression` Optional input argument. This argument takes on a number between 0 and 4095 controlling the amount of compression to try to achieve with PNG file. 0 implies no compresson, fastest option. 4095 implies the most amount of compression, slowest option. Default value is 8. + +## Speed and File Size Comparison + +Note: PngEncoder in the figure and tables below is [objectplanet's PngEncoder](http://objectplanet.com/pngencoder/), which is the only other fast alternative I was able to find. + +![alt text](https://raw.github.com/stefslon/savepng/master/Benchmark_Results.png "Performance Comparison") + +### Save Time [sec] + +| Quality | IMWRITE | PNGENCODER | SAVEPNG | Improvement | +| ---- | ---- | ---- | ---- | ---- | +| 1 | 0.141927 | 0.064678 | 0.025635 | 5.727868 | +| 4 | 0.127750 | 0.052499 | 0.022406 | 5.733489 | +| 8 | 0.132577 | 0.055713 | 0.022846 | 5.821804 | +| 64 | 0.128273 | 0.052453 | 0.029980 | 4.288976 | +| 4095 | 0.128502 | 0.052923 | 0.046395 | 2.777765 | + +### File Size [bytes] + +| Quality | IMWRITE | PNGENCODER | SAVEPNG | Improvement | +| ---- | ---- | ---- | ---- | ---- | +| 1 | 69394.53 | 107448.83 | 96119.83 | 0.727982 | +| 4 | 69394.53 | 107448.83 | 85958.17 | 0.811632 | +| 8 | 69394.53 | 107448.83 | 81293.93 | 0.856658 | +| 64 | 69394.53 | 107448.83 | 63869.70 | 1.087186 | +| 4095 | 69394.53 | 107448.83 | 58686.30 | 1.180958 | diff --git a/external/savepng.cpp b/external/savepng.cpp new file mode 100755 index 0000000..7a03443 --- /dev/null +++ b/external/savepng.cpp @@ -0,0 +1,133 @@ +// % SAVEPNG +// % Very fast PNG image compression routine. +// % +// % Input syntax is: +// % savepng(CDATA,filename[,Compression]); +// % +// % Optional parameters: +// % Compression A number between 0 and 4095 controlling the amount of +// % compression to try to achieve with PNG file. 0 implies +// % no compresson, fastest option. 4095 implies the most +// % amount of compression, slowest option. Default +// % value is 8. +// % +// % Example: +// % img = getframe(gcf); +// % savepng(img.cdata,'example.png'); +// % +// % PNG encoding routine based on public-domain MINIZ library: +// % http://code.google.com/p/miniz/ +// % +// +// % Author: S.Slonevskiy, 02/18/2013 +// % File bug reports at: +// % https://github.com/stefslon/savepng/issues +// +// % Versions: +// % 02/18/2013, Initial version + +#include +#include + +#include "mex.h" +#include "matrix.h" + +#include "miniz.cpp" + + +/* The gateway function */ +void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + unsigned char *imgdata; /* packed raw pixel matrix */ + unsigned char *indata; /* input image data matrix */ + unsigned width, height; /* size of matrix */ + unsigned char max_probes; /* compression level */ + const mwSize *dim_array; + unsigned x, y, chan, idx; + unsigned char *outdata; + FILE* file; + + char *filename; + size_t filenamelen; + size_t filelen; + + /* Default number of probes */ + max_probes = 8; + + /* Check for proper number of arguments */ + if(nrhs<2) { + mexErrMsgIdAndTxt("savepng:nrhs","At least two inputs required."); + } + + /* Check if compression level is commanded */ + if(nrhs==3) { + max_probes = mxGetScalar(prhs[2]); + } + + /* Check probes range */ + if((max_probes<0) || (max_probes>4095)) { + mexErrMsgIdAndTxt("savepng:nrhs","Compression level must be between 0 and 4095."); + } + + /* Get the number of dimensions in the input argument. */ + dim_array = mxGetDimensions(prhs[0]); + void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int max_probes, int num_chans, size_t *pLen_out); + + if((!mxIsUint8(prhs[0])) || (mxGetNumberOfDimensions(prhs[0])!=3) || (dim_array[2]!=3)) { + mexErrMsgIdAndTxt("savepng:nrhs","Input must in the image data format of MxNx3 matrix of uint8."); + } + + /* Pointer to image input data */ + indata = (unsigned char *)mxGetPr(prhs[0]); + + /* Get dimensions of input matrices */ + height = mxGetM(prhs[0]); + width = mxGetN(prhs[0])/3; /* this dimension concatenates with the 3rd dimension */ + + if(!mxIsUint8(prhs[0])) { + mexErrMsgIdAndTxt("savepng:nrhs","Input must in the image data format of MxNx3 matrix of uint8."); + } + + /* Fetch output filename */ + filenamelen = mxGetN(prhs[1])*sizeof(mxChar)+1; + filename = (char *)mxMalloc(filenamelen); + mxGetString(prhs[1], filename, (mwSize)filenamelen); + + /* some debug information */ + // mexPrintf("Input dimensions %d by %d \n",width,height); + // mexPrintf("Dimensions from array %d, %d, %d \n",dim_array[0],dim_array[1],dim_array[2]); + // mexPrintf("Input filename %s \n",filename); + + /* Convert MatLab image to raw pixels */ + imgdata = (unsigned char *)mxMalloc(width * height * 3); + idx = 0; + for(y = 0; y < height; y++) + { + for(x = 0; x < width; x++) + { + imgdata[idx++] = indata[x*height + y]; /* red */ + imgdata[idx++] = indata[1*width*height + x*height + y]; /* green */ + imgdata[idx++] = indata[2*width*height + x*height + y]; /* blue */ + //imgdata[idx++] = 255; /* alpha */ + } + } + + /* Encode PNG in memory */ + // Parameter "3" implies RGB pixel format + outdata = (unsigned char *)tdefl_write_image_to_png_file_in_memory((void *)imgdata, width, height, max_probes, 3, &filelen); + + /* Write to file */ + file = fopen(filename, "wb" ); + if(!file) return; + fwrite((char*)outdata, 1, filelen, file); + fclose(file); + + /* When finished using image data and filename string, deallocate it. */ + mxFree(filename); + mxFree(imgdata); + mxFree(outdata); + return; + +} + + diff --git a/external/savepng.m b/external/savepng.m new file mode 100644 index 0000000..64bbc67 --- /dev/null +++ b/external/savepng.m @@ -0,0 +1,35 @@ +function savepng(CDATA,filename,varargin) %#ok +% SAVEPNG +% Very fast PNG image compression routine. +% +% Input syntax is: +% savepng(CDATA,filename[,Compression]); +% +% Optional parameters: +% Compression A number between 0 and 4095 controlling the amount of +% compression to try to achieve with PNG file. 0 implies +% no compresson, fastest option. 4095 implies the most +% amount of compression, slowest option. Default +% value is 8. +% +% Example: +% img = getframe(gcf); +% savepng(img.cdata,'example.png'); +% +% PNG encoding routine based on public-domain MINIZ library: +% http://code.google.com/p/miniz/ +% + +% Author: S.Slonevskiy, 02/18/2013 +% File bug reports at: +% https://github.com/stefslon/savepng/issues + +% Versions: +% 02/18/2013, Initial version + +% Compile string +try + mex savepng.cpp -DMINIZ_NO_TIME -DMINIZ_NO_ARCHIVE_APIS -DMINIZ_NO_ARCHIVE_WRITING_APIS -DMINIZ_NO_ZLIB_APIS -DMINIZ_NO_ZLIB_COMPATIBLE_NAMES +catch + error('Sorry, auto-compilation failed.'); +end diff --git a/external/savepng.mexa64 b/external/savepng.mexa64 new file mode 100755 index 0000000..af22613 Binary files /dev/null and b/external/savepng.mexa64 differ diff --git a/external/savepng.mexmaci64 b/external/savepng.mexmaci64 new file mode 100755 index 0000000..d6cbb0a Binary files /dev/null and b/external/savepng.mexmaci64 differ diff --git a/external/savepng.mexw64 b/external/savepng.mexw64 new file mode 100644 index 0000000..f05489d Binary files /dev/null and b/external/savepng.mexw64 differ diff --git a/pipeline_scripts/hcp_anatomy.m b/pipeline_scripts/hcp_anatomy.m new file mode 100644 index 0000000..2f40d77 --- /dev/null +++ b/pipeline_scripts/hcp_anatomy.m @@ -0,0 +1,944 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; +ft_default.checksize = inf; % we need to retain the shapemri, will be otherwise cleared +ft_default.trackconfig = 'silent'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('subjectid', 'var') + error('subjectid should be specified') +elseif isnumeric(subjectid) + subjectid = num2str(subjectid); +end + +fprintf('executing the anatomy pipeline for subject %s\n', subjectid); + +if ~exist('outputdir', 'var') + outputdir = pwd; + fprintf('using %s as the directory to save the results\n', outputdir); +end + +if ~exist('structuralpreprocdir', 'var') + % we cannot use the strucural preprocessing results, revert to old-style + % pipeline + fprintf('not using the high quality structural preprocessing results\n'); +else + fprintf('using the structural preprocessing results from %s\n', structuralpreprocdir); + hrmrifile = fullfile(structuralpreprocdir, 'T1w', 'T1w_acpc_dc_restore.nii'); + inputsurffile = fullfile(structuralpreprocdir, 'T1w', 'fsaverage_LR32k', [subjectid,'.L.midthickness.32k_fs_LR.surf.gii']); + inputsphere = fullfile(structuralpreprocdir, 'MNINonLinear', 'fsaverage_LR32k', [subjectid,'.L.sphere.32k_fs_LR.surf.gii']); + outputsphere = fullfile(outputdir, 'Sphere.8k.L.surf.gii'); + outputsurffile = fullfile(outputdir, [subjectid,'.L.midthickness.8k_fs_LR.surf.gii']); +end + +% the following flags pertain to the three main parts of the pipeline +if ~exist('dopipeinteractive', 'var') + % for the interactive coregistration + dopipeinteractive = 0; +end +if ~exist('dopipeautomatic', 'var') + % for all other computations + dopipeautomatic = 0; +end +if ~exist('doqualitycheck', 'var') + % for the qualitycheck + doqualitycheck = 0; +end + +if dopipeinteractive, + % we need a pointer to a file in the dicom-series that contain the + % T1-weighted anatomical image + if ~exist('dofiducials', 'var') + dofiducials = 1; + end + if ~exist('dolandmarks', 'var') + dolandmarks = 1; + end + if (dofiducials || dolandmarks) && ~exist('dicomfile', 'var') + error('for the interactive part of the anatomy pipeline, a pointer to a file from the dicom series is needed'); + end + if ~exist('docoregistration', 'var') + docoregistration = 1; + end +end + +if dopipeautomatic, + % set flags to facilitate debugging or running parts of the pipeline + if ~exist('doheadmodel', 'var') + doheadmodel = 1; + end + if ~exist('dosourcemodel3d', 'var') + dosourcemodel3d = 1; + end + if ~exist('dosourcemodel2d', 'var') + dosourcemodel2d = 1; + end + if ~exist('dofreesurfer', 'var') + dofreesurfer = 0; + end + if ~exist('domnesuite', 'var') + domnesuite = 0; + end + + if dosourcemodel3d && ~exist('gridresolution', 'var') + % specify a default grid resolution + gridresolution = [4 6 8]; + end + + if doheadmodel && ~exist('headmodelthr', 'var') + headmodelthr = 0.5; + end + if doheadmodel && ~exist('headmodelsmooth', 'var') + headmodelsmooth = 5; + end + + if dosourcemodel2d && ~exist('structuralpreprocdir', 'var') && ~exist('mnepath', 'var') + error('when computing the cortical sheet based source model the path to MNE-suite software needs to be specified as ''mnepath'''); + end + if dosourcemodel2d && ~exist('structuralpreprocdir', 'var') && ~exist('surffile', 'var') + error('when computing the cortical sheet based source model a file pointer to the high resolution cortical sheets needs to be provided as ''surffile'''); + end + if dosourcemodel2d && ~exist('structuralpreprocdir', 'var') && ~exist('surflabeldir', 'var') + error('when computing the cortical sheet based source model a pointer to the directory where the labeling of the surfaces is stored needs to be provided as ''surflabeldir'''); + end + + % we need a pointer to an hs-file + if docoregistration && ~exist('hsfile', 'var') + error('for the automatic coregistration a pointer to an hsfile is needed'); + end +end + +%-------------------- +% declare outputfiles +outputprefix = fullfile(outputdir, [subjectid,'_MEG_anatomy']); +nifti_anatomical = [outputprefix,'_anatomical.nii']; +textfile_fiducials = [outputprefix,'_fiducials.txt']; +textfile_landmarks = [outputprefix,'_landmarks.txt']; +textfile_transform = [outputprefix,'_transform.txt']; +%-------------------------- + + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% HERE THE COMPUTATIONS START + +%% The following is the interactive part of the pipeline +if dopipeinteractive, + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Running the interactive part of the anatomy pipeline\n'); + fprintf('\n'); + + %--------------------------------------------- + % execute the interactive coregistration steps + if dolandmarks, + mriorig = ft_read_mri(dicomfile); + mri = mriorig; + + % coregister to anterior commissure based RAS space + fprintf('Please identify the Anterior Commissure, Posterior Commissure, a point on the positive Z and X axes, and a point on the right part of the head\n'); + cfg = []; + cfg.interactive = 'yes'; + mri = ft_volumerealign(cfg, mriorig); + landmarks = mri.cfg.landmark; + landmarks.coordsys = 'vox'; + hcp_write_ascii(textfile_landmarks, 'landmarks'); + end + + if dofiducials, + mriorig = ft_read_mri(dicomfile); + mri = mriorig; + + % coregister to subject headspace + fprintf('Please identify the LPA, RPA, nasion, and a point on the positive Z-axis\n'); + cfg = []; + cfg.interactive = 'yes'; + mri = ft_volumerealign(cfg, mriorig); + fiducials = mri.cfg.fiducial; + fiducials.coordsys = 'vox'; + hcp_write_ascii(textfile_fiducials, 'fiducials'); + + % save the pre-registered anatomical to a nifti file + cfg = []; + cfg.parameter = 'anatomy'; + cfg.filename = nifti_anatomical; + cfg.filetype = 'nifti'; + ft_volumewrite(cfg, mri); + end + + %-------------------------------- + % coregistration + if docoregistration + + % check that at least the nifti with the anatomy exists + if exist(nifti_anatomical, 'file') + mri = ft_read_mri(nifti_anatomical); + else + error('for the automatic part of the anatomy pipeline a nifti file containing the anatomy is needed: run the interactive part of the pipeline first and/or ensure that the nifti file created in this step is in the output directory'); + end + + if exist(textfile_fiducials, 'file') + hcp_read_ascii(textfile_fiducials); + else + error('for the automatic coregistration a textfile with fiducial locations needs to exist'); + end + if exist(textfile_landmarks, 'file') + hcp_read_ascii(textfile_landmarks); + else + error('for the automatic coregistration a textfile with landmark locations needs to exist'); + end + + % check which version of the coregistration needs to be run: if the + % option-list contains a key: filename_hr, a high-resolution, ACPC-space + % aligned volume exists, use this one for coregistration + + mriorig = mri; + % mriorig.transform = eye(4); % FIXME does this need to be done? + % what when voxels are not isotropic + + % do the coregistration to MNI space + fprintf('\n'); + fprintf('Coregistering the anatomy to the axes of the MNI coordinate system\n'); + fprintf('\n'); + + cfg = []; + cfg.landmark = landmarks; + mri = ft_volumerealign(cfg, mriorig); + + if exist('hrmrifile', 'var') && exist(hrmrifile, 'file') + % coregister the low-resolution MRI to the high resolution in + % ACPC-space; landmarks are not needed. + fprintf('\n'); + fprintf('Coregistering the low-resolution anatomy to the high-resolution anatomy\n'); + fprintf('\n'); + + % do the coregistration to MNI space + targetmri = ft_read_mri(hrmrifile); + targetmri.coordsys = 'spm'; + + cfg = []; + cfg.method = 'spm'; + cfg.spm.regtype = 'rigid'; + %cfg.method = 'fsl'; + %cfg.fsl.reslice = 'no'; + %cfg.fsl.searchrange = [-90 90]; + mri = ft_volumerealign(cfg, mri, targetmri); + + transform.vox2spm_interactive = mri.transformorig; + transform.vox2spm_registered = mri.transform; + end + + % set the transformation matrix + transform.vox2spm = mri.transform; + + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); + fprintf('Coregistering the anatomy to the axes of the MEG coordinate system\n'); + fprintf('\n'); + + % do an initial coregistration to BTI space + cfg = []; + cfg.fiducial = fiducials; + mri = ft_volumerealign(cfg, mriorig); + + transform.vox2bti_interactive = mri.transform; + + % refine the coregistration by doing a icp-based coregistration using + % the hs_file and the scalp surface reconstructed from the 1mm anatomy + fprintf('\n'); + fprintf('Refining the coregistration using the headshape file\n'); + fprintf('\n'); + + cfg = []; + cfg.method = 'headshape'; + cfg.headshape = ft_read_headshape(hsfile); + % weight the points below the xy-plane and on the forehead more + cfg.weights = ones(size(cfg.headshape.pnt,1),1); + cfg.weights(cfg.headshape.pnt(:,3)<0) = 1.5; + cfg.weights(cfg.headshape.pnt(:,3)>0.08 & cfg.headshape.pnt(:,3)<0.1) = 1.5; + cfg.weights(cfg.headshape.pnt(:,1)>0.05) = 2; + cfg.weights(cfg.headshape.pnt(:,1)<-0.05) = 2; + cfg.scalpsmooth = 'no'; + cfg.scalpthreshold = 0.08; + mri = ft_volumerealign(cfg, mri); + + headshape = struct(mri.cfg.headshape); % convert back from config object + headshape.coordsys = 'bti'; + headshapemri = struct(mri.cfg.headshapemri); + headshapemri.coordsys = 'bti'; + + mrifid.pnt = ft_warp_apply(mri.transform, [fiducials.nas;fiducials.lpa;fiducials.rpa]); + mrifid.label = {'NZinteractive';'Linteractive';'Rinteractive'}; + headshapemri.fid = mrifid; + + % write the headshapes + hcp_write_matlab([outputprefix,'_headshape'], 'headshape'); + hcp_write_matlab([outputprefix,'_headshapemri'], 'headshapemri'); + + % set the transformation matrix + transform.vox2bti_registered = mri.transform; + transform.vox2bti = mri.transform; + + % create some additional transformation matrices + transform.bti2vox = inv(transform.vox2bti); + transform.spm2vox = inv(transform.vox2spm); + + transform.spm2bti = transform.vox2bti/transform.vox2spm; + transform.bti2spm = transform.vox2spm/transform.vox2bti; + + % write the transform structure + hcp_write_ascii(textfile_transform, 'transform', transform, '%% FIXME put some comment here'); + + % quality check for the coregistration between headshape and mri + headshape = hcp_ensure_units(headshape, 'mm'); + headshapemri = hcp_ensure_units(headshapemri, 'mm'); + + figure; + subplot('position',[0.01 0.51 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.7 0.7 0.7],'fidcolor','y','facealpha',0.3); + ft_plot_mesh(headshapemri,'edgecolor','none','vertexcolor',headshapemri.distance); view(180,-90); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + subplot('position',[0.51 0.51 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.7 0.7 0.7],'fidcolor','y','facealpha',0.3); + ft_plot_mesh(headshapemri,'edgecolor','none','vertexcolor',headshapemri.distance); view(0,90); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + subplot('position',[0.01 0.01 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.7 0.7 0.7],'fidcolor','y','facealpha',0.3); + ft_plot_mesh(headshapemri,'edgecolor','none','vertexcolor',headshapemri.distance); view(90,0); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + subplot('position',[0.51 0.01 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.7 0.7 0.7],'fidcolor','y','facealpha',0.3); + ft_plot_mesh(headshapemri,'edgecolor','none','vertexcolor',headshapemri.distance); view(0,0); colorbar('east'); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + axis on; + grid on; + set(gcf,'color','w') + hcp_write_figure([outputprefix,'_headshape_distance.png'], gcf, 'resolution', 500); close all; + + v = headshapemri.pnt; + f = headshapemri.tri; + [f,v]=reducepatch(f,v, 0.2); + headshapemri.pnt = v; + headshapemri.tri = f; + + figure; + subplot('position',[0.01 0.51 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.5 0.6 0.8],'fidcolor','y','facealpha',0.3); + ft_plot_headshape(headshape,'vertexsize',3); view(180,-90); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + subplot('position',[0.51 0.51 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.5 0.6 0.8],'fidcolor','y','facealpha',0.3); + ft_plot_headshape(headshape,'vertexsize',3); view(0,90); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + subplot('position',[0.01 0.01 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.5 0.6 0.8],'fidcolor','y','facealpha',0.3); + ft_plot_headshape(headshape,'vertexsize',3); view(90,0); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + subplot('position',[0.51 0.01 0.48 0.48]);hold on; + ft_plot_mesh(headshapemri,'edgecolor','none','facecolor',[0.5 0.6 0.8],'fidcolor','y','facealpha',0.3); + ft_plot_headshape(headshape,'vertexsize',3); view(0,0); + plot3([-130 130],[0 0],[0 0],'k');plot3([0 0],[-120 120],[0 0],'k');plot3([0 0],[0 0],[-100 150],'k'); + axis on; + grid on; + set(gcf,'color','w') + hcp_write_figure([outputprefix,'_headshape.png'], gcf, 'resolution', 500); close all; + + % create figures at the landmarks' locations, for QC + crosshair=@(pos)plot([-100 100],pos(2),'y',pos(1)*[1 1],[-100 100],'y'); + cfg = []; + cfg.locationcoordinates = 'voxel'; + cfg.location = landmarks.ac; + cfg.interactive = 'no'; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_landmarks_ac.png'], gcf, 'resolution', 500); close; + cfg.location = landmarks.pc; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_landmarks_pc.png'], gcf, 'resolution', 500); close; + cfg.location = landmarks.xzpoint; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_landmarks_xzpoint.png'], gcf, 'resolution', 500); close; + cfg.location = landmarks.rpoint; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_landmarks_rpoint.png'], gcf, 'resolution', 500); close; + + % create figures at the fiducials' location, for QC + cfg = []; + cfg.locationcoordinates = 'voxel'; + cfg.location = fiducials.lpa; + cfg.interactive = 'no'; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_fiducials_lpa.png'], gcf, 'resolution', 500); close; + cfg.location = fiducials.rpa; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_fiducials_rpa.png'], gcf, 'resolution', 500); close; + cfg.location = fiducials.nas; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_fiducials_nas.png'], gcf, 'resolution', 500); close; + cfg.location = fiducials.zpoint; + figure;ft_sourceplot(cfg, mri); + hcp_write_figure([outputprefix,'_fiducials_zpoint.png'], gcf, 'resolution', 500); close; + + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); + end + + fprintf('Here ends the interactive part of the anatomy pipeline\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); +end + +%% The following is the non-interactive/automatic part of the pipeline +if dopipeautomatic, + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Running the non-interactive part of the anatomy pipeline\n'); + fprintf('\n'); + + % check that at least the nifti with the anatomy exists + if exist(nifti_anatomical, 'file') + mri = ft_read_mri(nifti_anatomical); + else + error('for the automatic part of the anatomy pipeline a nifti file containing the anatomy is needed: run the interactive part of the pipeline first and/or ensure that the nifti file created in this step is in the output directory'); + end + + if exist(textfile_transform, 'file') + hcp_read_ascii(textfile_transform); + else + error('when the automatic part of the anatomy pipeline is run without coregistration, a textfile with coregistration information needs to exist'); + end + + mri4D = mri; mri4D.transform = transform.vox2bti; mri4D.coordsys = 'bti'; + mriRAS = mri; mriRAS.transform = transform.vox2spm; mriRAS.coordsys = 'spm'; + + %--------------------------------------------- + % execute the non-interactive part of the pipeline + + % specify some variables and options + mnitemplate = fullfile(outputdir, 'T1.nii'); % external file assumed to be present in the outputdirectory + tpm = {fullfile(outputdir, 'grey.nii') fullfile(outputdir, 'white.nii') fullfile(outputdir, 'csf.nii')}; + + % ensure the environment variables to be defined + if dofreesurfer + % obsolete + end + + if dosourcemodel2d && domnesuite + p = getenv('MNE_ROOT'); + if isempty(p) + setenv('MNE_ROOT', mnepath); + end + end + + + %------------------------------ + % create volume conductor model + if doheadmodel + % single shell for MEG + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Creating a single shell volume conduction model for MEG forward modelling\n'); + fprintf('\n'); + + % perform the segmentation + cfg = []; + cfg.tpm = tpm; + cfg.template = mnitemplate; + cfg.output = 'brain'; + cfg.brainthreshold = headmodelthr; + cfg.brainsmooth = headmodelsmooth; + seg = ft_volumesegment(cfg, mri4D); + + % create the boundary + cfg = []; + cfg.method = 'singleshell'; + cfg.numvertices = 5000; + headmodel = ft_prepare_headmodel(cfg, seg); + headmodel.coordsys = 'bti'; + headmodel = ft_convert_units(headmodel); + + % write the headmodel + hcp_write_matlab([outputprefix,'_headmodel'], 'headmodel'); + + % create figures for qualitycheck + hcp_read_ascii(textfile_fiducials); + hcp_read_ascii(textfile_transform); + fiducials = hcp_ensure_units(fiducials, 'mm'); + fiducials = hcp_ensure_coordsys(fiducials, transform, 'bti'); + xyz = [fiducials.nas;fiducials.lpa;fiducials.rpa]; + headmodel = hcp_ensure_units(headmodel, 'mm'); + + figure; + subplot(2,2,1); hold on; view(180,-90); ft_plot_vol(headmodel); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + subplot(2,2,2); hold on; view(0,90); ft_plot_vol(headmodel); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + subplot(2,2,3); hold on; view(90, 0); ft_plot_vol(headmodel); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + subplot(2,2,4); hold on; view(0, 0); ft_plot_vol(headmodel); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + axis on; grid on; + hcp_write_figure([outputprefix,'_headmodel.png'], 'format', 'png'); + hcp_write_figure([outputprefix,'_headmodel.fig'], 'format', 'fig'); + + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); + + % qualitycheck for the headmodel + + end + + %----------------------- + % create source model(s) + if dosourcemodel3d + % 3D grid + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Creating 3-dimensional dipole grid for source modelling with beamformers\n'); + fprintf('\n'); + + for k = 1:numel(gridresolution) + fprintf('Grid resolution: %s\n\n',num2str(gridresolution(k))); + + templategridname = fullfile(outputdir,['standard_sourcemodel3d',num2str(gridresolution(k)),'mm']); + + % create the sourcemodel + hcp_read_matlab(templategridname); + cfg = []; + cfg.grid.warpmni = 'yes'; + cfg.grid.nonlinear = 'yes'; + cfg.grid.templatemri = mnitemplate; + cfg.grid.template = sourcemodel; + + cfg.mri = mri4D; + sourcemodel3d = ft_prepare_sourcemodel(cfg); + + % remove the mri-structure from grid.cfg + sourcemodel3d.cfg = rmfield(sourcemodel3d.cfg, 'mri'); + + % ensure correct units and coordinate system + sourcemodel3d.coordsys = 'bti'; + sourcemodel3d = ft_convert_units(sourcemodel3d); + + % write the sourcemodels + hcp_write_matlab([outputprefix,'_sourcemodel_3d',num2str(gridresolution(k)),'mm'], 'sourcemodel3d'); + + % create figure for qualitycheck + hcp_read_ascii(textfile_fiducials); + hcp_read_ascii(textfile_transform); + fiducials = hcp_ensure_units(fiducials, 'mm'); + fiducials = hcp_ensure_coordsys(fiducials, transform, 'bti'); + xyz = [fiducials.nas;fiducials.lpa;fiducials.rpa]; + sourcemodel3d = hcp_ensure_units(sourcemodel3d, 'mm'); + + figure; + subplot(2,2,1); hold on; view(180,-90); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + subplot(2,2,2); hold on; view(0,90); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + subplot(2,2,3); hold on; view(90,0); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + subplot(2,2,4); hold on; view(0,0); ft_plot_mesh(sourcemodel3d.pos(sourcemodel3d.inside,:)); plot3(xyz(:,1),xyz(:,2),xyz(:,3),'b*'); + axis on; grid on; + hcp_write_figure([outputprefix,'_sourcemodel_3d',num2str(gridresolution(k)),'mm.png'], 'format', 'png'); + hcp_write_figure([outputprefix,'_sourcemodel_3d',num2str(gridresolution(k)),'mm.fig'], 'format', 'fig'); + + end + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); + end + + if dosourcemodel2d + % 2D cortical sheet + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Creating dipole grid based on the cortical sheet\n'); + fprintf('\n'); + + % if structural_preproc output exists, use this + if exist('structuralpreprocdir', 'var') + % use wb_command to resample the 32k_LR registered meshes to a lower + % resolution + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Using workbench to downsample subject specific registered surfaces\n'); + fprintf('\n'); + + str = ['wb_command -surface-resample ',inputsurffile,' ',inputsphere,' ',outputsphere,' BARYCENTRIC ',outputsurffile]; + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.L', '.R'); + fprintf('executing %s\n', str); + system(str); + + % also do the inflated surfaces + str = strrep(str, 'midthickness', 'inflated'); + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.R', '.L'); + fprintf('executing %s\n', str); + system(str); + + % also do the sulc and curvature (just in case we need it) + % str = strrep(str, '.inflated', '.curv'); + % str = strrep(str, '.surf', '.shape'); + % str = strrep(str, 'resample-surface', 'resample-metric'); + % fprintf('executing %s\n', str); + % system(str); + % + % str = strrep(str, '.L', '.R'); + % fprintf('executing %s\n', str); + % system(str); + % + % str = strrep(str, 'curv', 'sulc'); + % fprintf('executing %s\n', str); + % system(str); + % + % str = strrep(str, '.R', '.L'); + % fprintf('executing %s\n', str); + % system(str); + + % also do the aparc files + inputlabelfile = strrep(inputsurffile, 'T1w', 'MNINonLinear'); + inputlabelfile = strrep(inputlabelfile, '.surf', '.label'); + inputlabelfile = strrep(inputlabelfile, '.midthickness', '.aparc'); + + outputlabelfile = strrep(outputsurffile, '.surf', '.label'); + outputlabelfile = strrep(outputlabelfile, '.midthickness', '.aparc'); + + str = ['wb_command -label-resample ',inputlabelfile,' ',inputsphere,' ',outputsphere,' BARYCENTRIC ',outputlabelfile]; + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.L', '.R'); + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.aparc', '.aparc.a2009s'); + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.R', '.L'); + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.aparc.a2009s', '.BA'); + fprintf('executing %s\n', str); + system(str); + + str = strrep(str, '.L', '.R'); + fprintf('executing %s\n', str); + system(str); + + sourcemodel2d = ft_read_headshape({outputsurffile strrep(outputsurffile,'.L','.R')}); + + % convert to mm units and change the coordinate system + sourcemodel2d = ft_convert_units(sourcemodel2d, 'mm'); + sourcemodel2d = ft_transform_geometry(transform.spm2bti, sourcemodel2d); + + % convert back to cm units + sourcemodel2d = ft_convert_units(sourcemodel2d, 'cm'); + + sourcemodel2d.pos = double(sourcemodel2d.pnt); + sourcemodel2d.tri = double(sourcemodel2d.tri); + sourcemodel2d = rmfield(sourcemodel2d, 'pnt'); + sourcemodel2d.coordsys = 'bti'; + + rootstr = 'midthickness'; % hack for backward compatibility + surflabeldir = outputdir; % these two are needed to get the parcellation info + else + % % we need to create the source space with MNE, using these mesh in + % % freesurfer format. These are in a different coordinate system, so we + % % need to adjust this. + % + % % get the transformation matrix + % bnd1 = ft_read_headshape(fullfile(pathname_hrfsdir,'surf','lh.white.surf.gii')); + % bnd2 = ft_read_headshape(fullfile(pathname_hrfsdir,'surf','lh.white')); + % + % pnt1 = double(bnd1.pnt(1:150:end,:)); + % pnt2 = double(bnd2.pnt(1:150:end,:)); + % T = [pnt2 ones(size(pnt2,1),1)]\[pnt1 ones(size(pnt1,1),1)]; + % T = T'; %transforms from 2 to 1 (i.e. FS to native) + % % The previous snippet of code is only needed when not working with the gifties + % % The gifties are already appropriately registered + + % only execute the lengthy creation of the high resolution 2D-grid if it has not yet been done. + if dofreesurfer + % segment the mri + cfg = []; + cfg.output = 'skullstrip'; + cfg.smooth = 5; + cfg.tpm = tpm; + cfg.template = mnitemplate; + seg = ft_volumesegment(cfg, mriRAS); + + % the mri and seg variables need to be written to disk in order for + % freesurfer to work out + cfg = []; + cfg.filetype = 'nifti'; + cfg.parameter = 'anatomy'; + cfg.filename = fullfile(pwd,subjectid); + ft_volumewrite(cfg, mriRAS); + cfg.filename = fullfile(pwd,[subjectid 'skullstrip']); + ft_volumewrite(cfg, seg); + + str = which('freesurferscript.sh'); + [p,f,e] = fileparts(str); + + % run the first part of the freesurfer pipeline + system([p,'/freesurferscript.sh ', pwd,' ', subjectid]); + end + + if domnesuite + % convert gifties into freesurfer format + if ft_filetype(surffile, 'caret_surf') + tmpdir = fullfile(outputdir, subjectid); + mkdir([tmpdir filesep 'surf']); + + [p,f,e] = fileparts(surffile); + tok = tokenize(f, '.'); + rootstr = tok{3}; + + tmpdir2 = fullfile(tmpdir, 'surf'); + tmpbnd = ft_read_headshape(surffile); + ft_write_headshape(fullfile(tmpdir2,['lh.',rootstr]), tmpbnd, 'format', 'freesurfer'); + tmpbnd = ft_read_headshape(strrep(surffile, '.L.', '.R.')); + ft_write_headshape(fullfile(tmpdir2,['rh.',rootstr]), tmpbnd, 'format', 'freesurfer'); + tmpbnd = ft_read_headshape(strrep(surffile, rootstr, 'sphere')); + ft_write_headshape(fullfile(tmpdir2,'lh.sphere'), tmpbnd, 'format', 'freesurfer'); + tmpbnd = ft_read_headshape(strrep(surffile, ['.L.',rootstr], ['.R.sphere'])); + ft_write_headshape(fullfile(tmpdir2,'rh.sphere'), tmpbnd, 'format', 'freesurfer'); + + else + % get the files that are needed temporarily at a location that + % mnesuite can deal with it. + tmpdir = fullfile(outputdir, subjectid); + mkdir(tmpdir); + [p,f,e] = fileparts(surffile); + system(['cp ' p filesep '* ' tmpdir filesep 'surf']); + rootstr = 'orig'; + end + + str = which('mnescript.sh'); + [p,f,e] = fileparts(str); + system([p,'/mnescript.sh ', outputdir,' ', subjectid,' ', rootstr]); + end + + % read in the source model + fname = fullfile(tmpdir,'bem',[subjectid,'-oct-6-src.fif']); + sourcemodel2d = ft_read_headshape(fname, 'format', 'mne_source'); + + % convert to mm units and change the coordinate system + sourcemodel2d = ft_convert_units(sourcemodel2d, 'mm'); + sourcemodel2d = ft_transform_geometry(transform.spm2bti, sourcemodel2d); + + % convert back to cm units + sourcemodel2d = ft_convert_units(sourcemodel2d, 'cm'); + + sourcemodel2d.pos = sourcemodel2d.pnt; + sourcemodel2d = rmfield(sourcemodel2d, 'pnt'); + sourcemodel2d.coordsys = 'bti'; + + % hack to let the following work + outputsurffile = surffile; + end + + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('Adding parcellation information to the cortical sheet source model\n'); + fprintf('\n'); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % get the three Freesurfer parcellations and add these to the sourcemodel + if ft_filetype(outputsurffile, 'caret_surf') + [p,f,e] = fileparts(outputsurffile); + tmpfilename = [f,e]; + tmpfilename = strrep(tmpfilename, rootstr, 'aparc'); + tmpfilename = strrep(tmpfilename, '.surf.', '.label.'); + tmpfile1 = fullfile(surflabeldir, tmpfilename); + tmpfile1b = outputsurffile; + tmpfilename = strrep(tmpfilename, rootstr, 'aparc'); + tmpfilename = strrep(tmpfilename, '.surf.', '.label.'); + tmpfilename = strrep(tmpfilename, '.L.', '.R.'); + tmpfile2 = fullfile(surflabeldir, tmpfilename); + tmpfile2b = strrep(outputsurffile, '.L.', '.R.'); + else + % this assumes the freesurfer directory to be visible + tmpfile1 = fullfile(surflabeldir,'lh.aparc.annot'); + tmpfile1b = outputsurffile; + tmpfile2 = fullfile(surflabeldir,'rh.aparc.annot'); + tmpfile2b = strrep(outputsurffile, 'lh.', 'rh.'); + end + atlasleft = ft_read_atlas({tmpfile1 tmpfile1b}, 'format', 'freesurfer_aparc'); + atlasright = ft_read_atlas({tmpfile2 tmpfile2b}, 'format', 'freesurfer_aparc'); + + % combine left and right atlases + nlabel = numel(atlasleft.aparclabel); + for k = 1:nlabel + aparclabel{k} = ['L_',atlasleft.aparclabel{k}]; + aparclabel{k+nlabel} = ['R_',atlasright.aparclabel{k}]; + end + + % update the indexing + tmp = atlasright.aparc; + tmp(tmp>0) = tmp(tmp>0)+nlabel; + aparc = cat(1, atlasleft.aparc, tmp); + + if isfield(sourcemodel2d, 'orig') + sourcemodel2d.aparc = aparc(sourcemodel2d.orig.inuse>0); + else + sourcemodel2d.aparc = aparc; + end + sourcemodel2d.aparclabel = aparclabel; + + if ft_filetype(outputsurffile, 'caret_surf') + [p,f,e] = fileparts(outputsurffile); + tmpfilename = [f,e]; + tmpfilename = strrep(tmpfilename, rootstr, 'aparc.a2009s'); + tmpfilename = strrep(tmpfilename, '.surf.', '.label.'); + tmpfile1 = fullfile(surflabeldir, tmpfilename); + tmpfile1b = outputsurffile; + tmpfilename = strrep(tmpfilename, rootstr, 'aparc.a2009s'); + tmpfilename = strrep(tmpfilename, '.surf.', '.label.'); + tmpfilename = strrep(tmpfilename, '.L.', '.R.'); + tmpfile2 = fullfile(surflabeldir, tmpfilename); + tmpfile2b = strrep(outputsurffile, '.L.', '.R.'); + else + tmpfile1 = fullfile(surflabeldir,'lh.aparc.a2009s.annot'); + tmpfile1b = outputsurffile; + tmpfile2 = fullfile(surflabeldir,'rh.aparc.a2009s.annot'); + tmpfile2b = strrep(outputsurffile, 'lh.', 'rh.'); + end + atlasleft = ft_read_atlas({tmpfile1 tmpfile1b}, 'format', 'freesurfer_a2009s'); + atlasright = ft_read_atlas({tmpfile2 tmpfile2b}, 'format', 'freesurfer_a2009s'); + + % combine left and right atlases + nlabel = numel(atlasleft.a2009slabel); + for k = 1:nlabel + a2009slabel{k} = ['L_',atlasleft.a2009slabel{k}]; + a2009slabel{k+nlabel} = ['R_',atlasright.a2009slabel{k}]; + end + + % update the indexing + tmp = atlasright.a2009s; + tmp(tmp>0) = tmp(tmp>0)+nlabel; + a2009s = cat(1, atlasleft.a2009s, tmp); + + if isfield(sourcemodel2d, 'orig') + sourcemodel2d.a2009s = a2009s(sourcemodel2d.orig.inuse>0); + else + sourcemodel2d.a2009s = a2009s; + end + sourcemodel2d.a2009slabel = a2009slabel; + + if ft_filetype(outputsurffile, 'caret_surf') + [p,f,e] = fileparts(outputsurffile); + tmpfilename = [f,e]; + tmpfilename = strrep(tmpfilename, rootstr, 'BA'); + tmpfilename = strrep(tmpfilename, '.surf.', '.label.'); + tmpfile1 = fullfile(surflabeldir, tmpfilename); + tmpfile1b = outputsurffile; + tmpfilename = strrep(tmpfilename, rootstr, 'BA'); + tmpfilename = strrep(tmpfilename, '.surf.', '.label.'); + tmpfilename = strrep(tmpfilename, '.L.', '.R.'); + tmpfile2 = fullfile(surflabeldir, tmpfilename); + tmpfile2b = strrep(outputsurffile, '.L.', '.R.'); + else + tmpfile1 = fullfile(surflabeldir,'lh.BA.annot'); + tmpfile1b = outputsurffile; + tmpfile2 = fullfile(surflabeldir,'rh.BA.annot'); + tmpfile2b = strrep(outputsurffile, 'lh.', 'rh.'); + end + atlasleft = ft_read_atlas({tmpfile1 tmpfile1b}, 'format', 'freesurfer_ba'); + atlasright = ft_read_atlas({tmpfile2 tmpfile2b}, 'format', 'freesurfer_ba'); + + % combine left and right atlases + nlabel = numel(atlasleft.BAlabel); + for k = 1:nlabel + BAlabel{k} = ['L_',atlasleft.BAlabel{k}]; + BAlabel{k+nlabel} = ['R_',atlasright.BAlabel{k}]; + end + + % update the indexing + tmp = atlasright.BA; + tmp(tmp>0) = tmp(tmp>0)+nlabel; + BA = cat(1, atlasleft.BA, tmp); + + if isfield(sourcemodel2d, 'orig') + sourcemodel2d.BA = BA(sourcemodel2d.orig.inuse>0); + else + sourcemodel2d.BA = BA; + end + sourcemodel2d.BAlabel = BAlabel; + + + % write the sourcemodel + hcp_write_matlab([outputprefix,'_sourcemodel2d'], 'sourcemodel2d'); + + fprintf('\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); + end + + fprintf('Here ends the non-interactive part of the anatomy pipeline\n'); + fprintf('-------------------------------------------------------------------------\n'); + fprintf('\n'); +end % dopipeautomatic + +%% The following is checking the quality of the output +if doqualitycheck, + % hcp_read_ascii(textfile_fiducials); % fiducials + % hcp_read_ascii(textfile_landmarks); % landmarks + % hcp_read_ascii(textfile_transform); % transform + % hcp_read_matlab([outputprefix,'_headshape']); + % hcp_read_matlab([outputprefix,'_headshapemri']); + % hcp_read_matlab([outputprefix,'_headmodel']); + % hcp_read_matlab([outputprefix,'_sourcemodel2d']); + % hcp_read_matlab([outputprefix,'_sourcemodel3d8mm']); + % mri = ft_read_mri(nifti_anatomical); + % mri.coordsys = 'bti'; + % mri.transform = transform.vox2bti; + % hcp_anatomy_qualitycheck(outputprefix, fiducials, landmarks, transform, mri, headshape, headshapemri, headmodel, sourcemodel2d, sourcemodel3d); + + % check whether the files exist FIXME + + %% The following is checking whether the output is present as expected + % ensure that the expected pipeline output is present + % see https://wiki.humanconnectome.org/display/EEG/MEG+anatomy + cd(outputdir); + hcp_check_pipelineoutput('anatomy', 'subject', subjectid); + +end diff --git a/pipeline_scripts/hcp_baddata.m b/pipeline_scripts/hcp_baddata.m new file mode 100644 index 0000000..fedfb6c --- /dev/null +++ b/pipeline_scripts/hcp_baddata.m @@ -0,0 +1,392 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +tok2=tokenize(scanid,'-'); +scanmnem=tok2{2}; + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +pipelineprefix='baddata'; +resultprefix = sprintf('%s_%s_%s', experimentid, scanid, pipelineprefix); + +% dsname = fileparts(filename); + +% Read in the raw data in one piece +cfg = []; +cfg.dataset = filename; +dataraw=ft_preprocessing(cfg); + +dataraw = ft_selectdata(dataraw,'channel','MEG'); + +Nsamples=size(dataraw.trial{1},2); + +isTask=strcmp(scanmnem,'Wrkmem')|strcmp(scanmnem,'Motort')|strcmp(scanmnem,'StoryM'); +isResting=strcmp(scanmnem,'Restin'); +%========================================================= +if isTask, + trialFunName=['trialfun_',scanmnem]; + trialDefFuncFile=['alltrialdefparams_',scanmnem]; + trialDefFuncHandle=str2func(trialDefFuncFile); + %------------------------------------------ + savesuffix_trialinfo='trialinfo'; + %--- Extract trials definition + allTrlCfgs=trialDefFuncHandle(); + Ncasegroups=length(allTrlCfgs); + outputTrialInfoSummaryFile = [resultprefix,'_raw',savesuffix_trialinfo,'_QC.txt']; + montage = hcp_exgmontage(subjectid, experimentid, scanid); + iCaseGroup=1; + %------------------------------------------ + trlCfg = allTrlCfgs{iCaseGroup}; + trlCfg.datafile = filename; + trlCfg.trialfun = trialFunName; + if ~isempty(regexp(scanid,'Motor')) + trlCfg.trialdef.montage=montage; + end + %------------------------------------------ + try + eval(['[trl,trlInfoColDescr,trialSummary,scanStartSamp,scanEndSamp,warninfo]=',trialFunName,'(trlCfg);']); + % Save the summary ascii file + hcp_write_ascii(outputTrialInfoSummaryFile,'trialSummary'); % Trial Summary should be the same no matter what the input arguments are. This is because the trial definition function creates all information about the trial definition. This is what the summary contains. The trl field only contains the trials defined by the input arguments. + ErrorFile=['ERROR_',outputTrialInfoSummaryFile]; + delete([ErrorFile,'*']); % Remove any error files present from previous runs + WarningFile=['WARNING_',outputTrialInfoSummaryFile]; + if isempty(warninfo) + delete([WarningFile,'*']); % Remove any warning files present from previous runs + else + hcp_write_ascii(WarningFile,'warninfo'); + end + + catch + ErrorMessage={'Something went wrong with trialdefinition function. hcp_baddata continued without any information from these functions i.e. manual bad segments at the start and end'}; + ErrorFile=['ERROR_',outputTrialInfoSummaryFile]; + delete([ErrorFile,'*']); + WarningFile=['WARNING_',outputTrialInfoSummaryFile]; + delete([WarningFile,'*']); % Remove any warning files present from previous runs + delete([outputTrialInfoSummaryFile,'*']); + hcp_write_ascii(ErrorFile,'ErrorMessage'); % Trial Summary should be the same no matter what the input arguments are. This is because the trial definition function creates all information about the trial definition. This is what the summary contains. The trl field only contains the trials defined by the input arguments. + scanStartSamp=nan; + scanEndSamp=nan; + end +elseif isResting, + idealDurSecs=5*60; % 5 minutes Restting state + actualDurSecs=(Nsamples./dataraw.fsample); + durDif=actualDurSecs-idealDurSecs; + if durDif<0, + scanStartSamp=nan; + scanEndSamp=nan; + error('The resting state scan is shorter than 5 minutes'); + elseif durDif>0, + frontPadSamps=floor((2/3)*durDif*dataraw.fsample); + idealDurSamps=floor(idealDurSecs*dataraw.fsample); + backPadSamps=Nsamples-frontPadSamps-idealDurSamps; + if backPadSamps<0, + error('There is a problem with splitting the excess time in 2/3 front and 1/3 back pad') + end + scanStartSamp=frontPadSamps; + scanEndSamp=frontPadSamps+idealDurSamps; + else + scanStartSamp=1; + scanEndSamp=Nsamples; + end + + + +end +%========================================================= +%========================================================= +%========================================================= +if ~exist([resultprefix '_manual_badsegments.txt'],'file'); + %-------------- + manual_badsegments=[]; + + if isTask|isResting + if scanStartSamp>1 + manual_badsegments=[manual_badsegments + [1 scanStartSamp] ]; + end + if scanEndSamp1 + manual_badsegments=[manual_badsegments + [1 scanStartSamp] ]; + end + if scanEndSamp. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +if ~exist('lfreq', 'var') + lfreq = 60; +end + +if ~exist('coildiameter', 'var') + % the radius is 9 mm + coildiameter = 0.018; +end + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% remove temporary local variables +clear tok + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +resultprefix = sprintf('%s_%s', experimentid, scanid); +dirname = fileparts(filename); + +% Perform a number of sanitychecks on a MEG dataset. +% +% The header and events are read and a textfile is generated providing some basic information about the dataset: +% - sampling frequency +% - length in seconds +% - number of recorded MEG channels +% - number of recorded REF channels +% - number of recorded EEG channels +% - number of Trigger channels +% - number of Response channels +% - number of events +% - additional event information +% - a quantification of head movement (comparing COH with COH2) +% +% Subsequently the data is read in and some basic quantification is done to assess +% the quality of the data. The following figures will be generated: +% - coregistration image, showing the hs_file (head surface) in +% combination with the sensor-array +% - power spectra of MEG channels (computed after chopping up the data in +% 1024 sample snippets +% - power spectra of MEGREF channels (computed after chopping up the data +% in 1024 sample snippets +% - power spectra of EEG channels (computed after chopping up the data in +% 1024 sample snippets +% - spatial topography of the power line noise + +% add the general pipeline name "datacheck" to the files that are created +output = fullfile(pipelinedatadir, [resultprefix '_datacheck']); + +filename = [dirname, '/c,rfDC']; +hsname = [dirname, '/hs_file']; + +hdr = ft_read_header(filename); +event = ft_read_event(filename); + +% provide some textual feedback: this should go into a text file +textfile = [output, '_info.txt']; +fid = hcp_fopen(textfile, 'w'); +if fid<0 + error('cannot open output file'); +end + +hcp_fprintf(fid, '\n%s : %s\n', 'datafile', filename); +hcp_fprintf(fid, '%s : %5.2f %s\n', 'sampling frequency ', hdr.Fs, 'Hz'); +hcp_fprintf(fid, '%s : %5.2f %s\n', 'length ', (hdr.nSamples*hdr.nTrials)/hdr.Fs, 'seconds'); +hcp_fprintf(fid, '%s : %d\n', 'number of MEG channels ', sum(strcmp(hdr.chantype, 'megmag'))); +hcp_fprintf(fid, '%s : %d\n', 'number of REF channels ', sum(strcmp(hdr.chantype, 'megref'))); +hcp_fprintf(fid, '%s : %d\n', 'number of EEG channels ', sum(strcmp(hdr.chantype, 'eeg'))); +hcp_fprintf(fid, '%s : %d\n', 'number of ECG channels ', sum(strcmp(hdr.chantype, 'ecg'))); +hcp_fprintf(fid, '%s : %d\n', 'number of EMG channels ', sum(strcmp(hdr.chantype, 'emg'))); +hcp_fprintf(fid, '%s : %d\n', 'number of TRIGGER channels ', sum(strcmp(hdr.chantype, 'trigger'))); +hcp_fprintf(fid, '%s : %d\n\n', 'number of events ', numel(event)); + +% show event table +if isempty(event) + hcp_fprintf(fid, 'no events were found in the datafile\n'); +else + % show some details + eventtype = unique({event.type}); + Neventtype = length(eventtype); + if Neventtype==0 + hcp_fprintf(fid, 'no events were found in the datafile\n'); + else + hcp_fprintf(fid, 'the following events were found in the datafile\n'); + for i=1:Neventtype + sel = find(strcmp(eventtype{i}, {event.type})); + try + eventvalue = unique({event(sel).value}); % cell-array with string value + eventvalue = sprintf('''%s'' ', eventvalue{:}); % translate into a single string + catch + eventvalue = unique(cell2mat({event(sel).value})); % array with numeric values or empty + eventvalue = num2str(eventvalue); % translate into a single string + end + hcp_fprintf(fid, 'event type: ''%s'' ', eventtype{i}); + hcp_fprintf(fid, 'with event values: %s', eventvalue); + hcp_fprintf(fid, '\n'); + end + end +end % isempty event + +% show head localization info +for k = 1:numel(hdr.orig.user_block_data) + type{k} = hdr.orig.user_block_data{k}.hdr.type; +end +sel = find(strcmp('B_COH_Points', type)); +if numel(sel)==2 + pnt1 = hdr.orig.user_block_data{sel(1)}.pnt; % Coils in dewar space + pnt2 = hdr.orig.user_block_data{sel(2)}.pnt; + + dpnt = sqrt(sum((pnt1-pnt2).^2,2))*1000; % amount of movement in mm + dpnt = mean(dpnt); + hcp_fprintf(fid, '\n'); + hcp_fprintf(fid, 'average coil movement: %s mm', num2str(dpnt, '%5.3f')); + hcp_fprintf(fid, '\n'); +else + hcp_fprintf(fid, '\n'); + hcp_fprintf(fid, 'no head coil information found'); + hcp_fprintf(fid, '\n'); +end + +hcp_fprintf(fid, '\n'); +hcp_fclose(fid); + +seleeg = ft_channelselection('E*', hdr.label); haseeg = ~isempty(seleeg); % FIXME: EEG as heuristic does not work for this set of labels, returning empty +selmeg = ft_channelselection('MEG', hdr.label); hasmeg = ~isempty(selmeg); +selref = ft_channelselection('MEGREF', hdr.label); hasref = ~isempty(selref); + +% get the layout for the MEG topography +cfg = []; +cfg.layout = '4D248.mat'; +lay = ft_prepare_layout(cfg); + +% plot sensors and headshape +if exist(hsname, 'file') + shape = ft_read_headshape(hsname); + f1 = figure; + a = [-0.15 0.15 -0.10 0.10 -0.10 0.15]; + subplot(2,2,1); ft_plot_headshape(shape); view([-90 90]); axis(a); % top + subplot(2,2,2); ft_plot_headshape(shape); view([ 00 00]); axis(a); % right + subplot(2,2,3); ft_plot_headshape(shape); view([-90 00]); axis(a); % back + subplot(2,2,4); hold on; ft_plot_headshape(shape, 'fidlabel', false, 'fidcolor', 'none'); view([0 0]); ft_plot_sens(hdr.grad, 'chantype', 'megmag', 'coildiameter', coildiameter); axis(a); + figurefile = [output, '_headshape']; + hcp_write_figure(figurefile, f1); + close(f1); +end + +% create a trl matrix +trl = (1:1024:(hdr.nSamples*hdr.nTrials))'; +trl(:,2) = min(trl(:,1)+1023, hdr.nSamples*hdr.nTrials); +trl(:,3) = 0; +% only keep the trials that are precisely 1024 samples, exclude left-overs at the end +trllen = trl(:,2)-trl(:,1)+1; +trl = trl(trllen==1024,:); + +% set up the cfgs for the preprocessing and spectral analysis +cfg = []; +cfg.datafile = filename; +cfg.feedback = 'none'; + +% some general preprocessing, no demeaning first to get the low freq stuff +cfg1 = []; +cfg1.demean = 'yes'; +cfg1.feedback = 'none'; + +% for the time course of the line noise +cfg2 = []; +cfg2.dftfilter = 'yes'; +cfg2.dftfreq = lfreq + [-1 0 1].*(hdr.Fs./1024); +cfg2.feedback = 'none'; +cfg2.dftinvert = 'yes'; +cfg2.rectify = 'yes'; +cfg2.boxcar = 0.2; + +% for the detection of jumps +cfg3 = []; +cfg3.absdiff = 'yes'; +cfg3.medianfilter = 'yes'; +cfg3.medianfiltord = 9; +cfg3.feedback = 'none'; + +% for the analysis of low frequency power +cfg4 = []; +cfg4.lpfilter = 'yes'; +cfg4.lpfreq = 2; +cfg4.feedback = 'none'; + +% for the spectral analysis +cfgf = []; +cfgf.method = 'mtmfft'; +cfgf.output = 'pow'; +cfgf.taper = 'hanning'; +cfgf.feedback = 'none'; + +% allocate some memory for later +minval = zeros(248,1)+inf; +maxval = zeros(248,1)-inf; +sumval = zeros(248,1); +ssqval = zeros(248,1); +covmat = zeros(248); + +if hasmeg, + for k = 1:200:size(trl,1) + % chop up the data in 1024 sample snippets; the correlation heuristic + % is likely to fail when the individual snippets are too long + % (correlation then mainly reflects the very low frequency fluctuations + % due to the environment + cfg.trl = trl(k:min(size(trl,1),k+199),:); + cfg.channel = selmeg; + tmpdata = ft_preprocessing(cfg); + tmpfreq = ft_freqanalysis(cfgf, tmpdata); + + % post-process the data for low-freq power <2Hz: before demeaning + tmp = ft_preprocessing(cfg4, tmpdata); + if k==1, + tmptrace0 = zeros(numel(tmp.label),0); + end + for m = 1:numel(tmp.trial) + tmptrace0 = cat(2, tmptrace0, var(tmp.trial{m},[],2)); + end + + % demean the data and compute some quantities + tmpdata = ft_preprocessing(cfg1, tmpdata); + for m = 1:numel(tmpdata.trial) + minval = min(minval, min(tmpdata.trial{m},[],2)); + maxval = max(maxval, max(tmpdata.trial{m},[],2)); + sumval = sumval + sum(tmpdata.trial{m},2); + covmat = covmat + tmpdata.trial{m}*tmpdata.trial{m}'; + ssqval = ssqval + sum(tmpdata.trial{m}.^2,2); + end + + % post-process the data line noise + tmp = ft_preprocessing(cfg2, tmpdata); + tmptmp = zeros(numel(tmp.label),0); + for m = 1:numel(tmp.trial) + if size(tmp.trial{m},2)<512 + continue; + elseif size(tmp.trial{m},2)<1024 + ix = 256; + else + ix = [256 768]; + end + tmptmp = cat(2, tmptmp, tmp.trial{m}(:,ix)); % sample two points of the smoothed linenoise time courses + end + if k==1, + powline = tmptmp; + else + powline = cat(2, powline, tmptmp); + end + % FIXME what to do with this? + + % post-process the data for squid jumps + tmp = ft_preprocessing(cfg3, tmpdata); + if k==1, + tmptrace1 = zeros(numel(tmp.label),0); + end + for m = 1:numel(tmp.trial) + tmptrace1 = cat(2, tmptrace1, max(tmp.trial{m},[],2)); + end + + % accumulate the power spectra + if k==1, + pow = tmpfreq.powspctrm.*numel(tmpdata.trial); + else + pow = pow + tmpfreq.powspctrm.*numel(tmpdata.trial); + end + end + pow = pow./size(trl,1); + + % plot for jumps + f1=figure; + plot(tmptrace1'); + xlabel('epoch (#)'); + ylabel('amplitude (T)'); + title('MEG squid jumps'); + figurefile = [output, '_jumps']; + hcp_write_figure(figurefile, f1); + close(f1); + + % plot for low frequency power + f1=figure; + plot(tmptrace0'); + xlabel('epoch (#)'); + ylabel('power (T^2/Hz)'); + title('MEG low frequency power'); + figurefile = [output, '_MEG_lowfreq_power']; + hcp_write_figure(figurefile, f1); + close(f1); + + % make a plot of the powerspectra + f1=figure; + loglog(tmpfreq.freq,pow); + abc = axis; + axis([1 tmpfreq.freq(end) abc(3:4)]); + xlabel('log10 frequency (Hz)'); + ylabel('log10 power (T^2/Hz)'); + title('powerspectra MEG'); + figurefile = [output, '_MEG_powspctrm']; + hcp_write_figure(figurefile, f1); + close(f1); + + % make a topographical plot of the power line noise + [a,b] = match_str(lay.label, tmpfreq.label); + c = nearest(tmpfreq.freq, lfreq); + + f1=figure; hold on; + lay.label{end-1} = 'power (T^2/Hz)'; + ft_plot_lay(lay, 'box', 'off'); + ft_plot_topo(lay.pos(a,1),lay.pos(a,2),pow(b,c),'gridscale',150,'outline',lay.outline,'mask',lay.mask,'interpmethod','nearest'); + axis([-0.6 0.6 -0.6 0.6]); + axis off; + abc = caxis; + caxis([-1 1]*abc(2)); + colorbar + title('power line noise on MEG sensors'); + figurefile = [output, '_MEG_powerline_noise']; + hcp_write_figure(figurefile, f1); + close(f1); +end % if hasmeg + +% if haseeg, +% for k = 1:200:size(trl,1) +% cfg.trl = trl(k:min(size(trl,1),k+199),:); +% cfg.channel = seleeg; +% tmpeeg = ft_preprocessing(cfg); +% tmpfreqe = ft_freqanalysis(cfgf, tmpeeg); +% +% if k==1, +% powe = tmpfreqe.powspctrm.*numel(tmpfreqe.cumtapcnt); +% else +% powe = powe + tmpfreqe.powspctrm.*numel(tmpfreqe.cumtapcnt); +% end +% end +% powe = powe./size(trl,1); +% +% f2=figure; +% loglog(tmpfreq.freq,powe); +% abc = axis; +% axis([1 tmpfreq.freq(end) abc(3:4)]); +% title('powerspectra EEG'); +% figurefile = [output, '_EEG_powspctrm']; +% hcp_write_figure(figurefile, f1); +% close(f1); +% end % if haseeg + +if hasref, + for k = 1:200:size(trl,1) + cfg.trl = trl(k:min(size(trl,1),k+199),:); + cfg.channel = selref; + tmpref = ft_preprocessing(cfg); + tmpfreqr = ft_freqanalysis(cfgf, tmpref); + + if k==1, + powr = tmpfreqr.powspctrm.*numel(tmpref.trial); + else + powr = powr + tmpfreqr.powspctrm.*numel(tmpref.trial); + end + end + powr = powr./size(trl,1); + + f1=figure; + loglog(tmpfreq.freq,powr); + abc = axis; + axis([1 tmpfreq.freq(end) abc(3:4)]); + xlabel('log10 frequency (Hz)'); + ylabel('log10 power (T^2/Hz)'); + title('powerspectra MEGREF'); + figurefile = [output, '_MEGREF_powspctrm']; + hcp_write_figure(figurefile, f1); + close(f1); +end % if hasref + +% compute mean value (meaningless) +nsmp = hdr.nSamples*hdr.nTrials; +meanval = sumval./nsmp; + +% compute variance and std +varval = (ssqval - (sumval.^2./nsmp))./(nsmp-1); +stdval = sqrt(varval); + +% compute channel correlation matrix +covmat = covmat - (sumval*sumval')./nsmp; +corrmat = covmat./sqrt(diag(covmat)*diag(covmat)'); + +% Set correlation to zeror for zero covariance +corrmat(~isfinite(corrmat)) = 0; + +% use Giorgos' strategy to assess noisy channels +cfg = []; +cfg.method = 'distance'; +cfg.neighbourdist = 0.035; +cfg.grad = hdr.grad; +neighb = ft_prepare_neighbours(cfg); +Nmat = zeros(248); +for k = 1:248 + [a,b] = match_str(hdr.grad.label(1:248), neighb(k).neighblabel); + Nmat(a,k) = 1; +end +corrmat(~Nmat) = nan; +C = nanmean(corrmat); + +% Considering different arrangment of labels in "lay.label" and "neighb.label" +% it's probably safe not to assume anything +[a,b] = match_str(lay.label, {neighb.label}); + +% plot the average neighbour correlation +f1=figure; hold on; +ft_plot_lay(lay, 'box', 'off'); +ft_plot_topo(lay.pos(a,1),lay.pos(a,2),C(b),'gridscale',150,'outline',lay.outline,'mask',lay.mask,'interpmethod','nearest'); +axis([-0.6 0.6 -0.6 0.6]); +axis off; +caxis([0.4 0.65]); +colorbar +title('average correlation with neighbours'); +figurefile = [output, '_neighb_correlation']; +hcp_write_figure(figurefile, f1); +close(f1); + +indTrigCh=find(strcmp('TRIGGER', hdr.label)); +indRespCh=find(strcmp('RESPONSE', hdr.label)); + +dat = ft_read_data(filename,'chanindx',indTrigCh); +trig = dat; +dat = ft_read_data(filename,'chanindx',indRespCh); +resp = dat; + +f1 = figure; +% Raw trigger, response channels +subplot(2,1,1), plot(trig); ylabel('TRIGGER'); set(gca,'xticklabel',[]);set(gca,'Position',[0.15 0.55 0.8 0.43]); axis tight; +subplot(2,1,2), plot(resp); ylabel('RESPONSE'); xlabel('sample');set(gca,'Position',[0.15 0.1 0.8 0.43]); axis tight; +figurefile = [output, '_triggers']; +hcp_write_figure(figurefile, f1); +close(f1); + +%% Process EOG/ECG/EMG and plot + +cfg = []; +cfg.dataset = filename; +dataraw = ft_preprocessing(cfg); + +montage = hcp_exgmontage(subjectid, experimentid, scanid); + +if isfield(montage, 'labelorg') && ~isempty(montage.labelorg) % This check is performed for cases where no Electrodes have been recorded (i.e Glasgow data) + hasELEC = 1; + dataELEC = ft_selectdata(dataraw,'channel',montage.labelorg); + dataELECnew = ft_apply_montage(dataELEC,montage); + + % Remove line noise ? + + cfg = []; + cfg.detrend = 'no'; + cfg.bsfilter = 'yes'; + cfg.bsfreq = lfreq+[-1 1]; + dataELECnew2 = ft_preprocessing(cfg, dataELECnew);clear dataELECnew; + + cfg = []; + cfg.detrend = 'no'; + cfg.hpfilter = 'yes'; + cfg.hpfreq = 0.25; + cfg.hpfiltord=4; + dataELECnew2 = ft_preprocessing(cfg, dataELECnew2); + + + cfg = []; + cfg.detrend = 'no'; + cfg.lpfilter = 'yes'; + cfg.lpfreq = 150; + dataELECnew2 = ft_preprocessing(cfg, dataELECnew2); + + %-freq analysis of elec channels + + + + %===================================================== + % The following code has been modified by francesco + + %{ + OLD CODE by RObert + cfgf = []; + cfgf.method = 'mtmfft'; + cfgf.output = 'pow'; + cfgf.taper = 'hanning'; + cfgf.feedback = 'text'; + cfgf.foi = 1:0.1:100; + cfgf.tapsmofrq = 0.1; + freqELEC = ft_freqanalysis(cfgf,dataELECnew2); + %} + + % NEW CODE by Francesco + win=2048/(dataELECnew2.fsample*2^(round(log2(1024/dataELECnew2.fsample)))); + + cfg = []; + cfg.length = win; + ref_data_seg = ft_redefinetrial(cfg, dataELECnew2); + + cfg=[]; + cfg.method = 'mtmfft'; + cfg.output = 'pow'; + cfg.trials = 'all'; + cfg.taper = 'hamming'; + + [freq_ref_data] = ft_freqanalysis(cfg,ref_data_seg); + + selec = sqrt(freq_ref_data.powspctrm); + + freqELEC =freq_ref_data; + freqELEC.powspctrm = selec ; + %===================================================== + + %--------------------------------------- + Nelecs=length(dataELECnew2.label); + for iElec=1:Nelecs, + hp1=figure; + set(hp1,'position',[10 100 6*200 200]); + hax1=subplot(1,2,1); + hax2=subplot(1,2,2); + set(hax1,'position',[0.05 0.175 0.7 0.7],'fontsize',8); + set(hax2,'position',[0.79 0.175 0.2 0.7],'fontsize',8); + axes(hax1); + plot(dataELECnew2.time{1}, dataELECnew2.trial{1}(iElec,:));axis tight; + xlabel('time(sec)');ylabel('signal');title([regexprep(freqELEC.label{iElec},'_','\\_'),' - Time series']); + axes(hax2); +% semilogx(freqELEC.freq, freqELEC.powspctrm(iElec,:));axis tight; + plot(freqELEC.freq, freqELEC.powspctrm(iElec,:));axis tight; +% hlabx=xlabel('frequency (Hz)');title(['Spectrum: Power vs log(freq.)']); + hlabx=xlabel('frequency (Hz)');title(['Spectrum: Power vs freq.']); + + axis([0 70 0 max(freqELEC.powspctrm(iElec,:))]) +% set(hax2,'XTick',[1 100]) + %set(hlabx,'Units','normalized') + %set(hlabx,'position',[0.15 -0.035 1]) + + %figpos=get(hp1,'position'); + %pappos=get(hp1,'paperpos'); + %pappos(4)=(figpos(4)./figpos(3))*pappos(3); + pappos=[0.25 1 6*2 2]; + set(hp1,'paperpos',pappos); + + figurefile = [output, '_elecchan_',freqELEC.label{iElec}]; + hcp_write_figure(figurefile, hp1); + close(hp1); + end +else + hasELEC=0; + display(['NO ELECTRODES WERE PROVIDED BY EXGMONTAGE for ', subjectid, '_', experimentid, '_', scanid]); +end + + +%% ensure that the expected pipeline output is present +hcp_check_pipelineoutput('datacheck', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); diff --git a/pipeline_scripts/hcp_eravg.m b/pipeline_scripts/hcp_eravg.m new file mode 100644 index 0000000..fd87e2f --- /dev/null +++ b/pipeline_scripts/hcp_eravg.m @@ -0,0 +1,329 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +%% INPUT +% Here 2 filenames are required for Working Memory and Story Math and 1 for +% Motor. So there are 2 variables + +if ~exist('filename1', 'var') + error('filename1 at least should be specified') +end + +if ~exist('filename2', 'var') + filename2=''; +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' + +tokF1 = tokenize(filename1, '/'); if ~isempty(filename2), tokF2 = tokenize(filename2, '/');end; + +if ~exist('subjectid', 'var') + subjectid1 = tokF1{end-7}; + if ~isempty(filename2), + subjectid2 = tokF2{end-7}; + if ~strcmp(subjectid1,subjectid2), + error('the two filenames provided are from different subjects'); + else + subjectid=subjectid1; + end + else + subjectid=subjectid1; + end +end + + +if ~exist('experimentid', 'var') + experimentid1 = tokF1{end-5}; + if ~isempty(filename2), + experimentid2 = tokF2{end-5}; + if ~strcmp(experimentid1,experimentid2), + error('the two filenames provided have the same subjectid BUT different experimentid'); + else + experimentid=experimentid1; + end + else + experimentid=experimentid1; + end +end +%------------------------------------------------ +if ~exist('scanid1', 'var') + scanid1 = tokF1{end-3}; +end +tmptok1 = tokenize(scanid1, '-'); +scanmnem1 = tmptok1{2}; +%------- The following is just for the cases where the suffix "_Run1 or +%Run2" has been added to the scanid in order to differentiate between 2 +%different runs of the same paradigm. i.e. The way Robert has saved data in +%his database for subject CP10168. +indRunStr=regexp(scanmnem1,'_Run'); +if ~isempty(indRunStr), + scanmnem1=scanmnem1(1:indRunStr(1)-1); +end + + +if ~exist('scanid2', 'var') + if ~isempty(filename2), + scanid2 = tokF2{end-3}; + else + scanid2=''; + end +end +if ~isempty(scanid2) + tmptok2 = tokenize(scanid2, '-'); + scanmnem2 = tmptok2{2}; + %------- The following is just for the cases where the suffix "_Run1 or + %Run2" has been added to the scanid in order to differentiate between 2 + %different runs of the same paradigm. i.e. The way Robert has saved data in + %his database for subject CP10168. + indRunStr=regexp(scanmnem2,'_Run'); + if ~isempty(indRunStr), + scanmnem2=scanmnem2(1:indRunStr(1)-1); + end +else + scanmnem2=''; +end; +%------------------------------------------------ + + + +if ~isempty(scanmnem2) + if ~strcmp(scanmnem1,scanmnem2) + error('the two scan mnemonics do not seem to agree'); + end +end +scanmnem=scanmnem1; +% %----- +% if ~exist('avgmode', 'var') +% avgmode={'mag','planar'}; % or planar +% end +% if ~iscell(avgmode) +% avgmode={avgmode}; +% end +% %------ +if ~exist('datagroupstr', 'var') + datagroupstr=[]; +end + +if ~exist('contrastidstr', 'var') + contrastidstr=[]; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%====================================================== +%% THOROUGH CHECKS OF INPUTS BEFORE ANALYSIS + +Nfiles=1+(~isempty(filename2)); + +%================================ +% Should this stop the pipeline? +if Nfiles==1, + if (~isempty( strfind( scanid1 , 'Wrkmem' ) ))|(~isempty( strfind( scanid1 , 'StoryM' ))) + disp('WARNING!!! Only 1 file provided for Working memory or Story Math'); + end +end +%================================ +contrastid=tokenize(contrastidstr, ','); +if isempty(contrastid{1}) + contrastid=''; +end + +datagroupid=tokenize(datagroupstr, ','); +if isempty(datagroupid{1}) + datagroupid=''; +end +if (~isempty(contrastid))&(~isempty(datagroupid)) + error('Both constrast and datagroup have been provided. The contrastid contains the datagroupid aswell'); +end + +resultprefix = sprintf('%s_%s', experimentid, scanmnem); +savesuffix_general='eravg'; + +% the location of the dataset, i.e. the c,rfDC file with full path + +%========================================= +%-- Load all contrasts lists +contrastfun = ['contrast_', scanmnem]; +%------------------ +for iFile=1:Nfiles, + eval(['scanid=scanid',num2str(iFile)]); + hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); % Checking for trialinfo only + inputTrialInfoFile = sprintf('%s_%s_tmegpreproc_trialinfo',experimentid, scanid); + hcp_read_matlab(inputTrialInfoFile,'trlInfo'); + + + eval(['cntrstList{iFile}=',contrastfun,'(trlInfo)']); +end +%-------------------------------------------- +% CHeck that the 2 all contrast lists have the same number of contrasts +% and extract all the datagroupids + +Nallcontr1=length(cntrstList{1}); +callnames1=[]; +pipenames=[]; +for iC=1:Nallcontr1, + callnames1{iC}=cntrstList{1}{iC}.mnemprint; + pipenames{iC}=cntrstList{1}{iC}.pipeline; +end +if Nfiles==2, + Nallcontr2=length(cntrstList{2}); + if Nallcontr1~=Nallcontr2 + error('The two files do not have the same number of contrasts in their all contrast lists'); + end + + callnames2=[];for iC=1:Nallcontr2, callnames2{iC}=cntrstList{2}{iC}.mnemprint;end +end +%-----------Find only the contrasts that are flagged for this pipeline +[indxPipe,ind2]=match_str(pipenames,'eravg'); + +if isempty(indxPipe) + error('No eravg constasts were found in the allcontrast list'); +else + + curPipecntrList{1}=cntrstList{1}(indxPipe); + curPipecallnames=callnames1(indxPipe); + if Nfiles==2, + curPipecntrList{2}=cntrstList{2}(indxPipe); + end +end +%--- End of checking the all contrast list +%------------------------------------------------------- +Neravgcontrs=length(curPipecntrList{1}); +%======================================================================= +%======================================================================= +%======================================================================= +%% FUSE all contrasts list with inputs (if present otherwise use all contrasts) +procCntrList=[]; +if ~isempty(contrastid) + NcontrIn=length(contrastid); + [indAll,indIn]=match_str(curPipecallnames,contrastid); + if length(indIn)~=NcontrIn + error(['Some contrast from the provided input were not found ']); + end + procCntrList{1}=curPipecntrList{1}(indAll); + procCntrNames=curPipecallnames(indAll); + if Nfiles==2, + procCntrList{2}=curPipecntrList{2}(indAll); + end +else + if isempty(datagroupid) + procCntrList=curPipecntrList; + procCntrNames=curPipecallnames; + else + procCntrList=[]; + procCntrNames=[]; + countCntr=1; + for iC=1:Neravgcontrs + iGr=1; + while iGr<=length(datagroupid) + + if strcmp(curPipecntrList{1}{iC}.lockmode,datagroupid{iGr}) + procCntrList{1}{countCntr}=curPipecntrList{1}{iC}; + procCntrNames{countCntr}=curPipecallnames{iC}; + if Nfiles==2, + procCntrList{2}{countCntr}=curPipecntrList{2}{iC}; + end + countCntr=countCntr+1; + iGr=length(datagroupid)+1; + + end + iGr=iGr+1; + end + end + end +end + +if isempty(procCntrList) + procCntrList=curPipecntrList; + procCntrNames=curPipecallnames; +end +%--- END OF constructing the contrast list +%========================================================================== +%--- END OF checking phase before analysis +%========================================================================== +%========================================================================== +%========================================================================== +%-- Check if clean data is available for the data groups involved +Nproccontr=length(procCntrNames); +procGroups=[]; +for iC=1:Nproccontr, + procGroups=unique([procGroups,procCntrList{1}{iC}.lockmode]); +end + +%--- Checking if the clean data is there +for iG=1:length(procGroups), + hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid1,'datagroup',procGroups{iG}); + if Nfiles==2, + hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid2,'datagroup',procGroups{iG}); + end +end + +%========================================================================== +%========================================================================== +% procCntrList; +% procCntrNames; +% procGroups; + +if Nfiles==1, + multiscanid={scanid1}; +elseif Nfiles==2, + multiscanid={scanid1 scanid2}; +end + +cfg = []; +cfg.contrastlist = procCntrList; +cfg.subjectid = subjectid; +cfg.experimentid = experimentid; +cfg.multiscanid = multiscanid; + +[outStatus] = hcp_eravg_contrasts(cfg); + +hcp_check_pipelineoutput('eravg', 'subject', subjectid, 'experiment', experimentid, 'scan', scanmnem,'avgmode',{'mag','planar'},'contrasts',procCntrNames); diff --git a/pipeline_scripts/hcp_icaclass.m b/pipeline_scripts/hcp_icaclass.m new file mode 100644 index 0000000..4c14a5f --- /dev/null +++ b/pipeline_scripts/hcp_icaclass.m @@ -0,0 +1,264 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +resultprefix = sprintf('%s_%s', experimentid, scanid); + +% ensure that the expected input data exists +hcp_check_pipelineoutput('baddata', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); + +cfg = []; +cfg.dataset = filename; +dataraw=ft_preprocessing(cfg); + +%%%%%%%%%%% do ICA %%%%%%%%%%%%%%%%% +badsegments = hcp_read_ascii([resultprefix '_baddata_badsegments.txt']); +% badsegments = badsegments.badsegment.all; + +disp('bad segments concatenation') +% collect the results in a structure +maxsmp2 = max(badsegments.badsegment.ica(:)); +maxsmp3 = max(badsegments.badsegment.manual(:)); +maxsmp = max([maxsmp2, maxsmp3]); +badsample = zeros(1,maxsmp); +if ~isempty(badsegments.badsegment.ica) + for j=1:size(badsegments.badsegment.ica,1) + badsample(badsegments.badsegment.ica(j,1):badsegments.badsegment.ica(j,2)) = 1; + end +end +if ~isempty(badsegments.badsegment.manual) + for j=1:size(badsegments.badsegment.manual,1) + badsample(badsegments.badsegment.manual(j,1):badsegments.badsegment.manual(j,2)) = 1; + end +end + +if ~isempty(badsample) + flank = diff([0 badsample 0]); + badsegment_ica=[]; + badsegment_ica(:,1) = find(flank== 1); + badsegment_ica(:,2) = find(flank==-1) - 1; + + badsegments.badsegment.ica = badsegment_ica; +end + +badsegments = badsegments.badsegment.ica; + +badchannels = hcp_read_ascii([resultprefix '_baddata_badchannels.txt']); +badchannels = badchannels.badchannel.all; + +sel_channels={'MEG'}; +grad=dataraw.grad; + +if ~(isempty([badchannels{:}])), + for ich=1:size(badchannels,2) + sel_channels(1,ich+1)={['-' badchannels{1,ich}]}; + end +end +bandpass = [1.3 150]; % band pass frequency +if strcmp(subjectid,'CP10128') || strcmp(subjectid,'CP10129') + bandstop = [49 51 ; 99 101]; % band stop frequency +else + bandstop = [59 61 ; 119 121]; % band stop frequency +end + +if ~exist('ica_iter', 'var') + if ~isempty(regexp(scanid,'Rest')) + ica_iter=20; + else + ica_iter=20; + end +end +ica_iter + +options = {'resultprefix', resultprefix, 'channels', sel_channels, 'ica_iterations', ica_iter, 'skipped_intervals', badsegments, 'bandpass', bandpass, 'bandstop', bandstop}; % GIORGOS +data_meg = hcp_ICA_preprocessing(dataraw, options); + +% do the computation +[iteration datain]= hcp_ICA_unmix(data_meg, options); +%provvisory +hcp_write_matlab([resultprefix '_icaiteration'],'iteration','options') + +%try +%---------------------------------------------- +% The hcp_exgmontage.m function should return the EOG ECG reference +% channels +montage = hcp_exgmontage(subjectid, experimentid, scanid); + +if ~isempty(montage.labelorg) % This check is performed for cases where no Electrodes have been recorded (i.e Glasgow data) + hasELEC=1; + dataELEC=ft_selectdata(dataraw,'channel',montage.labelorg); + dataELECnew=ft_apply_montage(dataELEC,montage); +else + hasELEC=0; + dataELECnew=[]; + display(['NO ELECTRODES WERE PROVIDED BY EXGMONTAGE for ', subjectid, '_', experimentid, '_', scanid]); +end + + + +%ref = montage.labelorg'; +%==================================================== +%==================================================== +%==================================================== +%==================================================== +%====================================================== +% ---- Check if the data is from MOTOR TASK for which no ECG EOG channels +% are available and construct pseudo ECG EOG channels based on topology +% templates. As we do not use EEG anymore in Phase 2 , MOTOR task data +% should also have EO/CG channels and this part should not be necessary +oldMotorExperimentIds={'CP10128_MEG_v1',... % Glasgow scan + 'CP10129_MEG_v1',... % Glasgow scan + 'CP10141_MEG_v1',... % There are 2 scans with 2 difference EMG electrode locations. For each scan there are 3 blocks for each hand and foot with 12 trials in each block. So each condition has only 36 trials. + 'CP10138_MEG',... % There are 6 different scan with variable ISI and time jittering in some. For each scan there are 3 blocks for each hand and foot with beween 8 to 12 trials in each block. So each condition has only 24 to 36 trials. + 'CP10167_MEG',... % 1 scan with the latest protocol. 8 blocks per hand and foot with 10 trials each. 1200 msec ISI. However there is no MRI + 'CP10113_MEG_v2'... % There are 2 different scans. NOt clear what the difference is between the two. There are 8 blocks for each hand and foot, with 10 trials per block. Unfortunately EMG signal is bad for both Hands and trials cannot be extracted based on the EMG signal + }; + +isMotorTask= ~isempty(regexp(scanid,'Motor')); +isScanWithOldMotor= ismember(experimentid,oldMotorExperimentIds); +if (isMotorTask && isScanWithOldMotor) || (hasELEC==0) + % TODO: In the case that there are no electrodes at all(i.e. Glasgow) or + % there are no ECOG(i.e. Motor Tasks) pseudo reference channels are + % extracted from the ICA decomposition in the variable iteration. + % A decision need to be made how these cases (mainly the Motor tasks) + % will be handled. Probably in these cases the classification in Brain + % and artifact components should be performed without using any reference channels + % and the ICs should be examined manually in order to assign heart and + % eye artifact related components. + templateICAFile='templatecomps.mat';% This fle contains the template topologies for EOCG ICs and is locates in the sandbox folder + + cfg=[]; + cfg.templateICAFile=templateICAFile; + cfg.outputfile=[experimentid,'_',scanid,'_icaclass_virtchan']; + %------------------------------------------------------------ + + + [pseudodataECG,pseudodataEOG] = hcp_pseudoeocgfromtopo(cfg,iteration,datain); + %------------------------ + icaref=[pseudodataECG.label,pseudodataEOG.label]; + ref_data=ft_appenddata([],pseudodataECG,pseudodataEOG); + options = ft_setopt(options, 'ref_channels', icaref); + options = ft_setopt(options, 'subject', resultprefix); + options_ref = ft_setopt(options, 'channels',icaref); + options = ft_setopt(options, 'grad', grad); % GIORGOS + clear templateICAFile pseudodataECG pseudodataEOG +else + + [tmpIndx1,tmpIndx2]=match_str(dataELECnew.label,{'ECG' 'VEOG' 'HEOG'}); + if ~isempty(tmpIndx1) + icaref=dataELECnew.label(tmpIndx1); + else + icaref=[]; + error(['NO ECG VEOG ot HEOG found in the dataset . Check data and/or montage for ',experimentid,'_',scanid]); + return; + end + clear tmpIndx1 tmpIndx2; + + options = ft_setopt(options, 'ref_channels', icaref); + options = ft_setopt(options, 'subject', resultprefix); + options_ref = ft_setopt(options, 'channels',icaref); + options = ft_setopt(options, 'grad', grad); % GIORGOS + ref_data = hcp_ICA_preprocessing(dataELECnew, options_ref); +end +%======================================================== +clear icaref options_ref; + +comp_class = hcp_ICA_RMEG_classification(ref_data,options,iteration,datain); + +hcp_ICA_plotclassification(comp_class,options,datain); + +hcp_write_matlab([resultprefix '_icaclass'],'comp_class','options') + +vs=[]; +vs.good=[]; +vs.bad=[]; +vs.total_ic_number=comp_class.class.total_ic_number; +vs.brain_ic_number=comp_class.class.brain_ic_number; +vs.brain_ic=comp_class.class.brain_ic; +vs.ecg_eog_ic= comp_class.class.ecg_eog_ic; +vs.flag=0; +vs.physio=[]; +hcp_write_ascii(sprintf('%s_icaclass.txt', resultprefix), 'vs'); + +clear iteration datain comp_class options; +clear resultprefix dataraw badsegments badchannels sel_channels grad bandpass bandstop ica_iter; +clear data_meg montage dataELEC dataELECnew ref_data; +clear oldMotorExperimentIds isMotorTask isScanWithOldMotor; +clear cfg; + +% ensure that the expected output files were created +hcp_check_pipelineoutput('icaclass', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); +%catch me +% save([resultprefix '_montageerror'],'resultprefix') +%end diff --git a/pipeline_scripts/hcp_icaclass_qc.m b/pipeline_scripts/hcp_icaclass_qc.m new file mode 100644 index 0000000..05a524b --- /dev/null +++ b/pipeline_scripts/hcp_icaclass_qc.m @@ -0,0 +1,206 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +resultprefix = sprintf('%s_%s', experimentid, scanid); + +% ensure that the expected input data exists +hcp_check_pipelineoutput('baddata', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); +hcp_check_pipelineoutput('icaclass', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); + +%%%%%%%%%%% read vs components %%%%%%%%%%%%%%%%% +if exist([resultprefix '_icaclass_vs.txt'],'file'); + hcp_read_ascii([resultprefix '_icaclass_vs.txt']); +elseif exist([resultprefix '_icaclass.txt'],'file'); + hcp_read_ascii([resultprefix '_icaclass.txt']); +else + vs=[]; + vs.good=[]; + vs.bad=[]; + vs.physio=[]; + hcp_write_ascii(sprintf('%s_icaclass_vs.txt', resultprefix), 'vs'); +end + +good = vs.good; +bad = vs.bad; +if(isfield(vs,'physio')) + physio=vs.physio; +else + physio=[]; + vs.physio=[]; +end +vs.flag=1; + +disp(['loading run ' resultprefix]) +load([resultprefix '_icaclass']) + +disp(['brain ics -> ' num2str(comp_class.class.brain_ic)]) +comp_class.class.brain_ic_vs=comp_class.class.brain_ic; +for i=1:size(good,2) + comp_class.class.brain_ic_vs(1,end+1)=good(1,i); + [junk indxb]=find(comp_class.class.ecg_eog_ic==good(1,i)); + comp_class.class.ecg_eog_ic(indxb)=[]; +end +comp_class.class.brain_ic_vs=unique(comp_class.class.brain_ic_vs); + +for i=1:size(bad,2) + [junk indxb]=find(comp_class.class.brain_ic_vs==bad(1,i)) + comp_class.class.brain_ic_vs(indxb)=[] +end +comp_class.class.brain_ic_vs_number=size(comp_class.class.brain_ic_vs,2); + +for i=1:size(physio,2) + comp_class.class.ecg_eog_ic(1,end+1)=physio(1,i); +end +comp_class.class.ecg_eog_ic=unique(comp_class.class.ecg_eog_ic); +comp_class.class.physio=physio; + +disp(['total ics number -> ' num2str(comp_class.class.total_ic_number)]) +vs.total_ic_number=comp_class.class.total_ic_number; +disp(['brain ics vs number -> ' num2str(comp_class.class.brain_ic_vs_number)]) +vs.brain_ic_vs_number=comp_class.class.brain_ic_vs_number; +disp(['brain ics vs -> ' num2str(comp_class.class.brain_ic_vs)]) +vs.brain_ic_vs=comp_class.class.brain_ic_vs; + +vs.ecg_eog_ic= comp_class.class.ecg_eog_ic; + +hcp_write_ascii(sprintf('%s_icaclass_vs.txt', resultprefix), 'vs'); + +disp(['modified vs-brain = ' num2str(good) ' vs-art = ' num2str(bad)]) + +disp(['saving ' resultprefix '_icaclass_vs']) +hcp_write_matlab([resultprefix '_icaclass_vs'],'comp_class','options') + + +cfg = []; +cfg.dataset = filename; +dataraw=ft_preprocessing(cfg); + +%%%%%%%%%%% do ICA %%%%%%%%%%%%%%%%% +badsegments = hcp_read_ascii([resultprefix '_baddata_badsegments.txt']); +% badsegments = badsegments.badsegment.all; + +disp('bad segments concatenation') +% collect the results in a structure +maxsmp2 = max(badsegments.badsegment.ica(:)); +maxsmp3 = max(badsegments.badsegment.manual(:)); +maxsmp = max([maxsmp2, maxsmp3]); +badsample = zeros(1,maxsmp); +if ~isempty(badsegments.badsegment.ica) + for j=1:size(badsegments.badsegment.ica,1) + badsample(badsegments.badsegment.ica(j,1):badsegments.badsegment.ica(j,2)) = 1; + end +end +if ~isempty(badsegments.badsegment.manual) + for j=1:size(badsegments.badsegment.manual,1) + badsample(badsegments.badsegment.manual(j,1):badsegments.badsegment.manual(j,2)) = 1; + end +end + +if ~isempty(badsample) + flank = diff([0 badsample 0]); + badsegment_ica=[]; + badsegment_ica(:,1) = find(flank== 1); + badsegment_ica(:,2) = find(flank==-1) - 1; + + badsegments.badsegment.ica = badsegment_ica; +end + +badsegments = badsegments.badsegment.ica; + +badchannels = hcp_read_ascii([resultprefix '_baddata_badchannels.txt']); +badchannels = badchannels.badchannel.all; + +sel_channels={'MEG'}; +grad=dataraw.grad; + + +if ~(isempty([badchannels{:}])), + for ich=1:size(badchannels,2) + sel_channels(1,ich+1)={['-' badchannels{1,ich}]}; + end +end +bandpass = [1.3 150]; % band pass frequency +if strcmp(subjectid,'CP10128') || strcmp(subjectid,'CP10129') + bandstop = [49 51 ; 99 101]; % band stop frequency +else + bandstop = [59 61 ; 119 121]; % band stop frequency +end + +options = {'subject',subjectid,'resultprefix', resultprefix, 'channels', sel_channels, 'skipped_intervals', badsegments, ... + 'grad', grad, 'bandpass', bandpass, 'bandstop', bandstop,'textsize',24}; +data_meg = hcp_ICA_preprocessing(dataraw, options); + +hcp_ica_plotreport(comp_class,options,data_meg); + diff --git a/pipeline_scripts/hcp_powavg.m b/pipeline_scripts/hcp_powavg.m new file mode 100644 index 0000000..627b80b --- /dev/null +++ b/pipeline_scripts/hcp_powavg.m @@ -0,0 +1,125 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' + +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +clear tok + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + % fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +hcp_check_pipelineoutput('rmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); + +resultprefix = sprintf('%s_%s', experimentid, scanid); +inputfile = [resultprefix,'_rmegpreproc']; +outputfile = [resultprefix,'_powavg']; + +hcp_read_matlab(inputfile); + +cfg = []; +cfg.foilim = [0 150]; +cfg.method = 'mtmfft'; +cfg.taper = 'hanning'; +cfg.keeptrials = 'no'; +freq = ft_freqanalysis(cfg, data); + +hcp_write_matlab(outputfile, 'freq'); + +cfg = []; +cfg.parameter = 'powspctrm'; +cfg.operation = 'log10'; +log10freq = ft_math(cfg, freq); + +cfg = []; +cfg.layout = '4D248.mat'; +cfg.xlim = [5 55]; +cfg.axes = 'no'; +cfg.box = 'yes'; + +figure +ft_multiplotER(cfg, log10freq); +h = title(resultprefix); set(h, 'interpreter', 'none'); +hcp_write_figure([outputfile '_multiplot']); +close + +% cfg = []; +% cfg.xlim = [5 50]; +% figure +% ft_singleplotER(cfg, log10freq); + +figure +plot(log10freq.freq, log10freq.powspctrm); +axis([5 55 -30 -26]); +xlabel('frequency (Hz)'); +ylabel('log10(power)'); +grid on +h = title(resultprefix); set(h, 'interpreter', 'none'); +hcp_write_figure([outputfile '_singleplot']); +close + +hcp_check_pipelineoutput('powavg', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); diff --git a/pipeline_scripts/hcp_rmegpreproc.m b/pipeline_scripts/hcp_rmegpreproc.m new file mode 100644 index 0000000..3336521 --- /dev/null +++ b/pipeline_scripts/hcp_rmegpreproc.m @@ -0,0 +1,100 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' + +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +clear tok + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +hcp_check_pipelineoutput('baddata', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); +hcp_check_pipelineoutput('icaclass', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); + +resultprefix = sprintf('%s_%s', experimentid, scanid); +badchansuffix = 'baddata_badchannels'; % mnemonic for file that contains bad channel and segment info +badsegmsuffix = 'baddata_badsegments'; % mnemonic for file that contains bad channel and segment info +icainfosuffix = 'icaclass'; % mnemonic to save results + +cfg = []; +cfg.dataset = filename; +cfg.trialfun = 'trialfun_Restin'; +cfg.trialdef.trialDuration = 2; +cfg = ft_definetrial(cfg); +cfg.trl(:,4) = nan; % hcp_extract_allfromrun assumes a trigger code to be present + +cfg.badchanfile = [resultprefix,'_',badchansuffix,'.txt']; +cfg.badsegmfile = [resultprefix,'_',badsegmsuffix,'.txt']; +cfg.icainfofile = [resultprefix,'_',icainfosuffix]; +cfg.montage = hcp_exgmontage(subjectid, experimentid, scanid); +cfg.lineFreq = [60 120]; +cfg.badsegmode = 'remfull'; +cfg.outputfile = [resultprefix,'_rmegpreproc']; + +hcp_extract_allfromrun(cfg); + +hcp_check_pipelineoutput('rmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); diff --git a/pipeline_scripts/hcp_tfavg.m b/pipeline_scripts/hcp_tfavg.m new file mode 100644 index 0000000..a132fcb --- /dev/null +++ b/pipeline_scripts/hcp_tfavg.m @@ -0,0 +1,344 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +%% INPUT +% Here 2 filenames are required for Working Memory and Story Math and 1 for +% Motor. So there are 2 variables + +if ~exist('filename1', 'var') + error('filename1 at least should be specified') +end + +if ~exist('filename2', 'var') + filename2=''; +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' + +tokF1 = tokenize(filename1, '/'); if ~isempty(filename2), tokF2 = tokenize(filename2, '/');end; + +if ~exist('subjectid', 'var') + subjectid1 = tokF1{end-7}; + if ~isempty(filename2), + subjectid2 = tokF2{end-7}; + if ~strcmp(subjectid1,subjectid2), + error('the two filenames provided are from different subjects'); + else + subjectid=subjectid1; + end + else + subjectid=subjectid1; + end +end + + +if ~exist('experimentid', 'var') + experimentid1 = tokF1{end-5}; + if ~isempty(filename2), + experimentid2 = tokF2{end-5}; + if ~strcmp(experimentid1,experimentid2), + error('the two filenames provided have the same subjectid DUT different experimentid'); + else + experimentid=experimentid1; + end + else + experimentid=experimentid1; + end +end +%------------------------------------------------ +if ~exist('scanid1', 'var') + scanid1 = tokF1{end-3}; +end +tmptok1 = tokenize(scanid1, '-'); +scanmnem1 = tmptok1{2}; +%------- The following is just for the cases where the suffix "_Run1 or +%Run2" has been added to the scanid in order to differentiate between 2 +%different runs of the same paradigm. i.e. The way Robert has saved data in +%his database for subject CP10168. +indRunStr=regexp(scanmnem1,'_Run'); +if ~isempty(indRunStr), + scanmnem1=scanmnem1(1:indRunStr(1)-1); +end + + +if ~exist('scanid2', 'var') + if ~isempty(filename2), + scanid2 = tokF2{end-3}; + else + scanid2=''; + end +end +if ~isempty(scanid2) + tmptok2 = tokenize(scanid2, '-'); + scanmnem2 = tmptok2{2}; + %------- The following is just for the cases where the suffix "_Run1 or + %Run2" has been added to the scanid in order to differentiate between 2 + %different runs of the same paradigm. i.e. The way Robert has saved data in + %his database for subject CP10168. + indRunStr=regexp(scanmnem2,'_Run'); + if ~isempty(indRunStr), + scanmnem2=scanmnem2(1:indRunStr(1)-1); + end +else + scanmnem2=''; +end; +%------------------------------------------------ +if ~isempty(scanmnem2) + if ~strcmp(scanmnem1,scanmnem2) + error('the two scan mnemonics do not seem to agree'); + end +end +scanmnem=scanmnem1; +%----- +if ~exist('avgmode', 'var') + avgmode={'mag','planar'}; % or planar +end +if ~iscell(avgmode) + avgmode={avgmode}; +end +%------ +if ~exist('datagroupstr', 'var') + datagroupstr=[]; +end + +if ~exist('contrastidstr', 'var') + contrastidstr=[]; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%====================================================== +%% THOROUGH CHECKS OF INPUTS BEFORE ANALYSIS + +Nfiles=1+(~isempty(filename2)); + +%================================ +% Should this stop the pipeline? +if Nfiles==1, + if (~isempty( strfind( scanid1 , 'Wrkmem' ) ))|(~isempty( strfind( scanid1 , 'StoryM' ))) + disp('WARNING!!! Only 1 file provided for Working memory or Story Math'); + end +end +%================================ +contrastid=tokenize(contrastidstr, ','); +if isempty(contrastid{1}) + contrastid=''; +end + +datagroupid=tokenize(datagroupstr, ','); +if isempty(datagroupid{1}) + datagroupid=''; +end +if ~isempty(contrastid) && ~isempty(datagroupid) + error('Both constrast and datagroup have been provided. The contrastid contains the datagroupid aswell'); +end + +resultprefix = sprintf('%s_%s', experimentid, scanmnem); +savesuffix_general='tfavg'; + +% the location of the dataset, i.e. the c,rfDC file with full path + +%========================================= +%-- Load all contrasts lists +contrastfun = ['contrast_', scanmnem]; +%------------------ +for iFile=1:Nfiles, + eval(['scanid=scanid',num2str(iFile)]); + hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); % Checking for trialinfo only + inputTrialInfoFile = sprintf('%s_%s_tmegpreproc_trialinfo',experimentid, scanid); + hcp_read_matlab(inputTrialInfoFile,'trlInfo'); + + + eval(['cntrstList{iFile}=',contrastfun,'(trlInfo)']); +end +%-------------------------------------------- +% CHeck that the 2 all contrast lists have the same number of contrasts +% and extract all the datagroupids + +Nallcontr1=length(cntrstList{1}); +callnames1=[]; +pipenames=[]; +for iC=1:Nallcontr1, + callnames1{iC}=cntrstList{1}{iC}.mnemprint; + pipenames{iC}=cntrstList{1}{iC}.pipeline; +end +if Nfiles==2, + Nallcontr2=length(cntrstList{2}); + if Nallcontr1~=Nallcontr2 + error('The two files do not have the same number of contrasts in their all contrast lists'); + end + + callnames2=[];for iC=1:Nallcontr2, callnames2{iC}=cntrstList{2}{iC}.mnemprint;end +end +%-----------Find only the contrasts that are flagged for this pipeline +[indxPipe,ind2]=match_str(pipenames,'tfavg'); + +if isempty(indxPipe) + error('No tfavg constasts were found in the allcontrast list'); +else + curPipecntrList{1}=cntrstList{1}(indxPipe); + curPipecallnames=callnames1(indxPipe); + if Nfiles==2, + curPipecntrList{2}=cntrstList{2}(indxPipe); + end +end +Ntfavgcontr=length(curPipecntrList{1}); + +%--- End of checking the all contrast list +%------------------------------------------------------- +%======================================================================= +%======================================================================= +%======================================================================= +%% FUSE all contrasts list with inputs (if present otherwise use all contrasts) +procCntrList=[]; +if ~isempty(contrastid) + NcontrIn=length(contrastid); + [indAll,indIn]=match_str(curPipecallnames,contrastid); + if length(indIn)~=NcontrIn + error(['Some contrast from the provided input were not found ']); + end + procCntrList{1}=curPipecntrList{1}(indAll); + procCntrNames=curPipecallnames(indAll); + if Nfiles==2, + procCntrList{2}=curPipecntrList{2}(indAll); + end +else + if isempty(datagroupid) + procCntrList=curPipecntrList; + procCntrNames=curPipecallnames; + else + procCntrList=[]; + procCntrNames=[]; + countCntr=1; + for iC=1:Ntfavgcontr + iGr=1; + while iGr<=length(datagroupid) + + if strcmp(curPipecntrList{1}{iC}.lockmode,datagroupid{iGr}) + procCntrList{1}{countCntr}=curPipecntrList{1}{iC}; + procCntrNames{countCntr}=curPipecallnames{iC}; + if Nfiles==2, + procCntrList{2}{countCntr}=curPipecntrList{2}{iC}; + end + countCntr=countCntr+1; + iGr=length(datagroupid)+1; + + end + iGr=iGr+1; + end + end + end +end + +if isempty(procCntrList) + procCntrList=curPipecntrList; + procCntrNames=curPipecallnames; +end + + +%--- END OF constructing the contrast list +%========================================================================== +%--- END OF checking phase before analysis +%========================================================================== +%========================================================================== +%========================================================================== +%-- Check if tfsens data is available for the data groups involved +Nproccontr=length(procCntrNames); +procGroups=[]; +for iC=1:Nproccontr, + procGroups=unique([procGroups,procCntrList{1}{iC}.lockmode]); +end + + +bandDef={'D', [0 4] + 'TH', [4 7] + 'A', [7 14] + 'Blow',[14 21] + 'Bhigh',[21 31] + 'Glow',[31 51] + 'Gmid',[51 76] + 'Ghigh', [76 101]}; + +for iG=1:length(procGroups), + for iBand=1:size(bandDef,1) + hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid1,'datagroup',procGroups{iG},'band',bandDef(:,1)); + if Nfiles==2, + hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid2,'datagroup',procGroups{iG},'band',bandDef(:,1)); + end + end +end + +%========================================================================== +%========================================================================== +% procCntrList; +% procCntrNames; +% procGroups; + +if Nfiles==1, + multiscanid={scanid1}; +elseif Nfiles==2, + multiscanid={scanid1 scanid2}; +end + +for iMode=1:length(avgmode) + + cfg = []; + cfg.avgmode = avgmode{iMode}; %'mag','planar' + cfg.contrastlist = procCntrList; + cfg.subjectid = subjectid; + cfg.experimentid = experimentid; + cfg.multiscanid = multiscanid; + cfg.bandinfo = bandDef; + + [outStatus] = hcp_tfavg_contrasts(cfg); + +end + +hcp_check_pipelineoutput('tfavg', 'subject', subjectid, 'experiment', experimentid, 'scan', scanmnem,'avgmode',avgmode,'contrasts',procCntrNames); diff --git a/pipeline_scripts/hcp_tmegpreproc.m b/pipeline_scripts/hcp_tmegpreproc.m new file mode 100644 index 0000000..ad35c2b --- /dev/null +++ b/pipeline_scripts/hcp_tmegpreproc.m @@ -0,0 +1,219 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) 2011-2014 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% setup the execution environment +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +opengl software; + +% ensure that the time and date of execution are not stored in the provenance information +global ft_default +ft_default.trackcallinfo = 'no'; + +% allow the user to specify the path where additional data is present, e.g. the channel layout or anatomy files +if exist('path', 'var') + addpath(path) +end + +if ~exist('filename', 'var') + error('filename should be specified') +end + +% the filename is assumed to be something like +% 'rawdatadir/Phase1MEG/Subjects/SUBJECTID/Experiments/SUBJECTID_MEG/Scans/1-Rnoise_MNN_V1/Resources/4D/c,rfDC' + +if ~exist('logfilename', 'var') % This parameter is used in the case that an additional Logfile is required to slipt the trials + logfilename=''; +end + +tok = tokenize(filename, '/'); + +if ~exist('subjectid', 'var') + subjectid = tok{end-7}; +end + +if ~exist('experimentid', 'var') + experimentid = tok{end-5}; +end + +if ~exist('scanid', 'var') + scanid = tok{end-3}; +end + +if ~exist('pipelinedatadir', 'var') + pipelinedatadir = hcp_pathdef; +end + +% print the matlab and megconnectome version to screen for provenance +ver('megconnectome') + +% print the value of all local variables to screen for provenance +w = whos; +w = {w.name}; +w = setdiff(w, {'w', 'ans'}); +for i=1:length(w) + fprintf(hcp_printstruct(w{i}, eval(w{i}))); +end + +% change to the location of the processed data (input and output) +cd(pipelinedatadir) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% execute the pipeline +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +hcp_check_pipelineoutput('baddata', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); +hcp_check_pipelineoutput('icaclass', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid); + +badchansuffix='baddata_badchannels'; % mnemonic for file that contains bad channel and segment info +badsegmsuffix='baddata_badsegments'; % mnemonic for file that contains bad channel and segment info +icainfosuffix='icaclass'; % mnemonic to save results + + +tok2 = tokenize(scanid, '-'); +scanmnem = tok2{2}; + +%------- The following is just for the cases where the suffix "_Run1 or +%Run2" has been added to the scanid in order to differentiate between 2 +%different runs of the same paradigm. i.e. The way Robert has saved data in +%his database for subject CP10168. +indRunStr=regexp(scanmnem,'_Run'); +if ~isempty(indRunStr), + scanmnem=scanmnem(1:indRunStr(1)-1); +end + + +resultprefix = sprintf('%s_%s', experimentid, scanid); + + +% the location of the dataset, i.e. the c,rfDC file with full path +fname = filename; +logfname=logfilename; +%====================================== + +%--- IS the following used anywhere????? +% if (strcmp(subjectid ,'CP10128')|strcmp(subjectid ,'CP10129')) +% cfg.lineFreq = [50 100]; +% else +% cfg.lineFreq = [60 120]; +% end +%---------------------------------------------- + + + +%========================================================== +%========================================================== +%----- Extract precleaned trialinfo for all data groups + +trialFunName=['trialfun_',scanmnem]; +%trialFunName=['testtrialfun_',scanmnem]; +trialDefFuncFile=['alltrialdefparams_',scanmnem]; +trialDefFuncHandle=str2func(trialDefFuncFile); +%------------------------------------------ +savesuffix_general='tmegpreproc'; +savesuffix_trialinfo='trialinfo'; +%--- Extract trials definition +%hcp_read_ascii(trialDefFile); % loads allTrlCfgs +allTrlCfgs=trialDefFuncHandle(); +Ncasegroups=length(allTrlCfgs); +outputTrialInfoFile = [resultprefix,'_',savesuffix_general,'_',savesuffix_trialinfo]; +%outputTrialInfoSummaryFile = [resultprefix,'_',savesuffix_general,'_raw',savesuffix_trialinfo,'_QC.txt']; +trlInfo=[]; +trlInfo.lockNames=[]; +trlInfo.lockTrl=[]; + +montage = hcp_exgmontage(subjectid, experimentid, scanid); + +for iCaseGroup=1:Ncasegroups + iCaseGroup + + trlCfg = allTrlCfgs{iCaseGroup}; + trlCfg.datafile = fname; + trlCfg.trialfun = trialFunName; + + trlInfo.lockNames{iCaseGroup}=trlCfg.trialdef.lockMnem; + + if ~isempty(regexp(scanid,'Motor')) + trlCfg.trialdef.montage=montage; + end + if ~isempty(logfname) + trlCfg.expResultsFile=logfname; + end + %---------------------------------------- + + %------------------------------------------ + eval(['[trl,trlInfoColDescr,trialSummary,scanStartSamp,scanEndSamp ]=',trialFunName,'(trlCfg);']); + %cfgDefTr = ft_definetrial(trlCfg); + %trl=cfgDefTr.trl; + + % if size(trl,2)>3, + % trialinfo=trl(:,4:end); + % else + % trialinfo=[]; + % end + trlInfo.lockTrl{iCaseGroup}=trl; + trlInfo.trlColDescr{iCaseGroup}=trlInfoColDescr; +end +%========================================================== +% Save the summary ascii file +%hcp_write_ascii(outputTrialInfoSummaryFile,'trialSummary'); % Trial Summary should be the same no matter what the input arguments are. This is because the trial definition function creates all information about the trial definition. This is what the summary contains. The trl field only contains the trials defined by the input arguments. +%========================================================== +%----- Extract and clean data groups + +Ncasegroups=length(trlInfo.lockTrl); + +cleanTrlInfo=trlInfo; +for iCaseGroup=1:Ncasegroups + iCaseGroup + + datagroupid=trlInfo.lockNames{iCaseGroup}; + + cfg = []; + cfg.badchanfile = [resultprefix,'_',badchansuffix,'.txt']; + cfg.badsegmfile = [resultprefix,'_',badsegmsuffix,'.txt']; + cfg.icainfofile = [resultprefix,'_',icainfosuffix]; + %cfg.caseprefix = resultprefix; + cfg.datafile = filename; + cfg.badsegmode = allTrlCfgs{iCaseGroup}.badsegmode; %tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. 'BU' case) + cfg.montage = montage; %hcp_exgmontage(subjectid, experimentid, scanid); + cfg.outputfile = [resultprefix,'_',savesuffix_general,'_',datagroupid]; + + + if strcmp(subjectid,'CP10128') || strcmp(subjectid,'CP10129') + cfg.lineFreq = [50 100]; + else + cfg.lineFreq = [60 120]; + end + + cfg.trl=trlInfo.lockTrl{iCaseGroup}; + + cleanTrl=hcp_extract_allfromrun(cfg); + cleanTrlInfo.lockTrl{iCaseGroup}=cleanTrl; + cleanTrlInfo.trlColDescr{iCaseGroup}=[trlInfo.trlColDescr{iCaseGroup}; [num2str(size(trlInfo.trlColDescr{iCaseGroup},1)+1),'. hastrialNANs']]; + + %hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid,'datagroupid',datagroupid); +end + +trlInfo=cleanTrlInfo; +%save(cleantrialinfofile,'trlInfo'); + + +hcp_write_matlab(outputTrialInfoFile,'trlInfo'); +%hcp_write_ascii(outputTrialInfoSummaryFile,'trialSummary'); % Trial Summary should be the same no matter what the input arguments are. This is because the trial definition function creates all information about the trial definition. This is what the summary contains. The trl field only contains the trials defined by the input arguments. + +hcp_check_pipelineoutput('tmegpreproc', 'subject', subjectid, 'experiment', experimentid, 'scan', scanid,'datagroup',trlInfo.lockNames); diff --git a/template/4D248.mat b/template/4D248.mat new file mode 100644 index 0000000..a8ddc8d Binary files /dev/null and b/template/4D248.mat differ diff --git a/template/README b/template/README new file mode 100644 index 0000000..5b37eb4 --- /dev/null +++ b/template/README @@ -0,0 +1,35 @@ +------------------------------------------------------------------------------- +template channel layout file 4D248.mat +------------------------------------------------------------------------------- +This template 2-D channel layout allows for visualization of the +248-channel 4D MEG data in a topographic layout, as detailed on +http://fieldtriptoolbox.org/tutorial/layout + +------------------------------------------------------------------------------- +template MRI file mni_icbm152_t1_tal_nlin_sym_09a.nii +------------------------------------------------------------------------------- +This template MRI nifti file has been downloaded from the website of the +McConnell Brain Imaging Centre. + +http://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin2009 + +It belongs to the ICBM 2009a Nonlinear Symmetric template which is a 1x1x1mm +template which includes T1w, T2w, PDw modalities, also T2 relaxometry (T2 +values calculated for each subject using single dual echo PD/T2 scan), and +tissue probabilities maps. This template also includes a lobe atlas used for +ANIMAL+INSECT segmentation, brain mask, eye mask and face mask. The specific +file contains the T1 weighted map derived from 152 structural images, averaged +together after high-dimensional nonlinear registration into the common MNI152 +co-ordinate system. This template file is suitable for plotting individual or +group brain activity maps in the MNI space. + +------------------------------------------------------------------------------- +template sourcemodels based on regular 3-dimensional grids in MNI-space: +standard_sourcemodel3d4mm.mat, standard_sourcemodel3d6mm.mat and +standard_sourcemodel3d8mm.mat +------------------------------------------------------------------------------- +The coordinates of the dipoles that are defined on these template grids (with +regular dipole spacing of 4,6 and 8 mm) are represented in standardised MNI-space. +The subject-specific dipole grids have been created using these templates, and +after applying a non-linear warp to match the individual brain. More information +can be found on http://fieldtriptoolbox.org/template/sourcemodel diff --git a/template/mni_icbm152_t1_tal_nlin_sym_09a.nii b/template/mni_icbm152_t1_tal_nlin_sym_09a.nii new file mode 100644 index 0000000..46ae4cc Binary files /dev/null and b/template/mni_icbm152_t1_tal_nlin_sym_09a.nii differ diff --git a/template/standard_sourcemodel3d4mm.mat b/template/standard_sourcemodel3d4mm.mat new file mode 100644 index 0000000..f92392e Binary files /dev/null and b/template/standard_sourcemodel3d4mm.mat differ diff --git a/template/standard_sourcemodel3d6mm.mat b/template/standard_sourcemodel3d6mm.mat new file mode 100644 index 0000000..d428ebf Binary files /dev/null and b/template/standard_sourcemodel3d6mm.mat differ diff --git a/template/standard_sourcemodel3d8mm.mat b/template/standard_sourcemodel3d8mm.mat new file mode 100644 index 0000000..4cffcb9 Binary files /dev/null and b/template/standard_sourcemodel3d8mm.mat differ diff --git a/trial_functions/alltrialdefparams_Motort.m b/trial_functions/alltrialdefparams_Motort.m new file mode 100644 index 0000000..a4c700b --- /dev/null +++ b/trial_functions/alltrialdefparams_Motort.m @@ -0,0 +1,30 @@ +function[allTrlCfgs]=alltrialdefparams_Motort() + +%lockMnem = 'TFLA': FLASH DRIVER ONSET , 'TEMG': EMG CHANNEL ONSET +allTrlCfgs=[]; +%------------------------ +%'TEMG': EMG CHANNEL ONSET +countCases=1; +tmpCfg=[]; +tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. If one trial spans an entire block case) +tmpCfg.trialdef.lockMnem ='TEMG'; +tmpCfg.trialdef.TrigBasedOnEMG = 'yes'; +tmpCfg.trialdef.cutMode = 'trials'; +tmpCfg.trialdef.prestimTime = 1.2; +tmpCfg.trialdef.poststimTime = 1.2; +tmpCfg.trialdef.montage=[]; % This is the montage containing the emg channels.To be loaded on the fly. In .labelnew there should {'EMG_LH','EMG_LF','EMG_RH','EMG_RF'} +allTrlCfgs{countCases}=tmpCfg; +%------------------------ +%'TFLA': FLASH DRIVER ONSET +countCases=countCases+1; +tmpCfg=[]; +tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. If one trial spans an entire block case) +tmpCfg.trialdef.lockMnem ='TFLA'; +tmpCfg.trialdef.TrigBasedOnEMG = 'no'; +tmpCfg.trialdef.cutMode = 'trials'; +tmpCfg.trialdef.prestimTime = 1.2; +tmpCfg.trialdef.poststimTime = 1.2; +tmpCfg.trialdef.montage=[]; % This is the montage containing the emg channels.To be loaded on the fly. In .labelnew there should {'EMG_LH','EMG_LF','EMG_RH','EMG_RF'} +allTrlCfgs{countCases}=tmpCfg; +%------------------------ + diff --git a/trial_functions/alltrialdefparams_StoryM.m b/trial_functions/alltrialdefparams_StoryM.m new file mode 100644 index 0000000..e63ad27 --- /dev/null +++ b/trial_functions/alltrialdefparams_StoryM.m @@ -0,0 +1,49 @@ +function[allTrlCfgs]=alltrialdefparams_StoryM() +%lockedOn = 'TEV': All Events - Onset of Story Sentences, Math number words, Math operand words, Option intro word,Option 1, Option OR, Option 2 (Fixed trial length) +% 'TRESP': Response (Fixed trial length) +% 'BU': Units (Stories or Math Problems including Option Interval) (Variable trial length) + +%------------------------ +%'TEV': +countCases=1; +tmpCfg=[]; +tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. 'BU' case) +tmpCfg.trialdef.lockMnem ='TEV'; +tmpCfg.trialdef.cutmode = 1; +tmpCfg.trialdef.prestimTime = 1.5; +tmpCfg.trialdef.poststimTime = 4; +allTrlCfgs{countCases}=tmpCfg; +%------------------------ +%------------------------ +%'TRESP' +countCases=countCases+1; +tmpCfg=[]; +tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. 'BU' case) +tmpCfg.trialdef.lockMnem ='TRESP'; +tmpCfg.trialdef.cutmode = 2; +tmpCfg.trialdef.prestimTime = 1.5; +tmpCfg.trialdef.poststimTime = 1.5; +allTrlCfgs{countCases}=tmpCfg; +%------------------------ +%------------------------ +%'BSENT' +countCases=countCases+1; +tmpCfg=[]; +tmpCfg.badsegmode='repnan'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. 'BU' case) +tmpCfg.trialdef.lockMnem ='BSENT'; +tmpCfg.trialdef.cutmode = 3; +tmpCfg.trialdef.prestimTime = 1; +tmpCfg.trialdef.poststimTime =1; +allTrlCfgs{countCases}=tmpCfg; +%------------------------ +%------------------------ +%'BU' +countCases=countCases+1; +tmpCfg=[]; +tmpCfg.badsegmode='repnan'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. 'BU' case) +tmpCfg.trialdef.lockMnem ='BU'; +tmpCfg.trialdef.cutmode = 4; +tmpCfg.trialdef.prestimTime = 1.5; +tmpCfg.trialdef.poststimTime =1.5; +allTrlCfgs{countCases}=tmpCfg; +%------------------------ diff --git a/trial_functions/alltrialdefparams_Wrkmem.m b/trial_functions/alltrialdefparams_Wrkmem.m new file mode 100644 index 0000000..0e09015 --- /dev/null +++ b/trial_functions/alltrialdefparams_Wrkmem.m @@ -0,0 +1,28 @@ +function[allTrlCfgs]=alltrialdefparams_Wrkmem() + +%lockedOn = 'TIM': TRIALS ON IMAGE ONSET, 'TRESP': TRIALS ON RESPONSE BUTTON ONSET +%------------------------ +%'TIM': TRIALS ON IMAGE ONSET +countCases=1; +tmpCfg=[]; +tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. If one trial spans an entire block case) +tmpCfg.trialdef.lockMnem ='TIM'; +tmpCfg.trialdef.lockedOn = 'Image'; +tmpCfg.trialdef.cutMode = 'trials'; +tmpCfg.trialdef.preStimTime = 1.5; +tmpCfg.trialdef.postStimTime = 2.5; +allTrlCfgs{countCases}=tmpCfg; +%------------------------ +%------------------------ +%'TRESP': TRIALS ON RESPONSE BUTTON ONSET +countCases=countCases+1; +tmpCfg=[]; +tmpCfg.badsegmode='remfull'; % This is used by tmeg_preproc to know if trials should be fully removed, or bad segments should be replaced by nans(i.e. If one trial spans an entire block case) +tmpCfg.trialdef.lockMnem ='TRESP'; +tmpCfg.trialdef.lockedOn = 'Response'; +tmpCfg.trialdef.cutMode = 'trials'; +tmpCfg.trialdef.preStimTime = 1.5; +tmpCfg.trialdef.postStimTime = 2.5; +allTrlCfgs{countCases}=tmpCfg; +%------------------------ + diff --git a/trial_functions/contrast_Motort.m b/trial_functions/contrast_Motort.m new file mode 100644 index 0000000..230aacf --- /dev/null +++ b/trial_functions/contrast_Motort.m @@ -0,0 +1,29 @@ +function[contrastList]=contrast_Motort(trialinfo) + +% This function produces a default list of contrasts for the Working Memory +% paradigm. It uses the 'trialinfo' input matrix which contains all the +% trial information for a SINGLE run (FIXME:append runs). +% Each item in the list is a structure with fields +% .mnemonic % encoded mnemonic +% .description= % cell array with detailed description of the contrast. +% .selection - indices of contrast trials from input trialinfo. +% .operation : type of comparison . can be 'average difference' or 'ratio' + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +contrastList=contrast_Motort_Basic(trialinfo); diff --git a/trial_functions/contrast_Motort_Basic.m b/trial_functions/contrast_Motort_Basic.m new file mode 100644 index 0000000..7b9d24e --- /dev/null +++ b/trial_functions/contrast_Motort_Basic.m @@ -0,0 +1,710 @@ +function[contrastList]=contrast_Motort_Basic(trlInfo) + +% This function produces a default list of contrasts for the Motor +% paradigm. It uses the 'trialinfo' input matrix which contains all the +% trial information for a SINGLE run (FIXME:append runs). +% Each item in the list is a structure with fields +% .mnemonic % encoded mnemonic +% .description= % cell array with detailed description of the contrast. +% .selection - indices of contrast trials from input trialinfo. +% .operation : type of comparison . can be 'average difference' or 'ratio' + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +lockNames=trlInfo.lockNames; +lockTrl=trlInfo.lockTrl; + +if length(lockNames)~=length(lockTrl) + error(['The number of groups does not match the number of trialinfo matrices']) +end + + + +% INDIVIDUAL CONDITIONS - extracting condition trial indices and mnemonic labels - +%============================================================================== +%--- LOCKED ON IMAGE ONSET ------------------------------- + +iTFLA=find(strcmp(lockNames,'TFLA')); +trialinfo=lockTrl{iTFLA}; + +% Case - Fixation +[seltfla_FIX,labtfla_FIX]=trialsplit_Motort(trialinfo, 6,[]); +% Case - Left Hand +[seltfla_LH,labtfla_LH]=trialsplit_Motort(trialinfo, 1,[]); +% Case - Right Foot +[seltfla_LF,labtfla_LF]=trialsplit_Motort(trialinfo, 2,[]); +% Case - Right Hand +[seltfla_RH,labtfla_RH]=trialsplit_Motort(trialinfo, 4,[]); +% Case - Right Foot +[seltfla_RF,labtfla_RF]=trialsplit_Motort(trialinfo, 5,[]); + + +iTEMG=find(strcmp(lockNames,'TEMG')); +if ~isempty(iTEMG) + hasEMG=1; + trialinfo=lockTrl{iTEMG}; + if ~isempty(trialinfo) + % Case - Fixation + [seltemg_FIX,labtemg_FIX]=trialsplit_Motort(trialinfo, 6,[]); + % Case - Left Hand + [seltemg_LH,labtemg_LH]=trialsplit_Motort(trialinfo, 1,[]); + % Case - Right Foot + [seltemg_LF,labtemg_LF]=trialsplit_Motort(trialinfo, 2,[]); + % Case - Right Hand + [seltemg_RH,labtemg_RH]=trialsplit_Motort(trialinfo, 4,[]); + % Case - Right Foot + [seltemg_RF,labtemg_RF]=trialsplit_Motort(trialinfo, 5,[]); + else + hasEMG=0; + disp(['WARNING!!! - trialinfo for TEMG lock mode is empty - Creating contrasts only for TFLA']); + end +else + hasEMG=0; +end + +%============================================================================= +%============================================================================= +%------- CREATING CONTRASTS =========================================== +% -- Here all the contrasts to be investigated for this specific paradigm +% -- are hard coded . Any new contrasts to be added, should added in the +% -- sequence below after the individual condition trial and label selection +% -- has been peformed above +%-------------------------------------------------- +%----------------------------- + + +cntrst = []; +%============================================================ +%============================================================ +%{ + %Main structure of a cntrst + + contr =[]; + contr.pipeline = []; + contr.lockmode = []; + contr.mnemtrl = []; + contr.freqband = []; + contr.freq = []; + contr.operation = []; + contr.timeperiods = []; + contr.timedef = []; + contr.baseline = []; + contr.baselinetype= []; + contr.connemetric = []; + contr.invfiltertype =[]; + contr.selection = []; + contr.description = []; + contr.mnemprint = []; +%} +%============================================================ +if hasEMG + LockModes={'TFLA' % Lock modes represent different pools of trial data. + 'TEMG'}; +else + LockModes={'TFLA'}; +end +Nlm=length(LockModes); + +%--- LockMode 'TFLASH' +tfavgTimes{1}=[-1.2:0.025:1.2]'; +srcTimesFine{1}=[-1.2:0.01:1.2]'; +srcTimesCoarseSing{1}=[0 0.3 + 0.3 0.6 + 0.6 0.9]; +srcTimesCoarseComp{1}=[-0.3 0 + 0 0.3 + 0.3 0.6 + 0.6 0.9]; +srcTimesCoarseCompFIX{1}=[0 0.3 + 0 0.3 + 0 0.3 + 0 0.3]; +basePeriod{1}=[-0.3 0]; + +if hasEMG + %--- LockMode 'TEMG' + tfavgTimes{2}=[-1.2:0.025:1.2]'; + srcTimesFine{2}=[-1.2:0.025:1.2]'; + srcTimesCoarseSing{2}=[0 0.3 + 0.3 0.6 + 0.6 0.9]; + srcTimesCoarseComp{2}=[-0.3 0 + 0 0.3 + 0.3 0.6 + 0.6 0.9]; + srcTimesCoarseCompFIX{2}=[0 0.3 + 0 0.3 + 0 0.3 + 0 0.3]; + basePeriod{2}=[-0.3 0]; +end +%======================================================================= +%======================================================================= +%======================================================================= +%======================================================================= +tfavgFreqs=[1:100]; +freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; +tmpSingCases={'LH','RH','LF','RF'}; +tmpCompCases={ 'LH' 'FIX' + 'RH' 'FIX' + 'LF' 'FIX' + 'RF' 'FIX'}; + +for iLM=1:Nlm, + + lmstr=lower(LockModes{iLM}); + + for iCase=1:length(tmpSingCases) + %-- All Stimuli in Trials cut in n + %---------------------------------------------ind---------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM}},... + 'baseline' , {basePeriod{iLM}},... % In tfavg the Baseline is used ONLY FOR PLOTTING + 'baselinetype' , 'diff',... % In tfavg the Baseline is used ONLY FOR PLOTTING + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + if hasEMG + % Quick way of defining the computation of corticomuscular + % coherence at sensor level. Can be usefull to examine the + % quality of EMG and to identify in which frequencies there is + % the highest coherence. + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'connemetric' , 'emgcoh',... % SPECIAL CASE - in tfavg only coherence with emg is computed to get an idea of the band of coherency + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM}},... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + end + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + if hasEMG + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'emgcoh',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {tfavgTimes{iLM}},... + 'timedef' , 'concat',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + end + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseSing{iLM}},... + 'timedef' , 'concat',... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + end + + end + + + + %============================================================ + %============================================================ + %========CONSTRASTS FOR COMPARISONS BETWEEN CONDITIONS ====== + %============================================================ + + for iGroup=1:size(tmpCompCases,1) + %------------------------------------------------------------- + % Not sure if it makes sens to compute eravg and favg for comparison + % between Feet or Hands and Fixation. It makes more sense to + % reference the power in the souce level to the power during + % fixation. Now srcavglcmv from these comparison also does not make + % sense with the comparison being applied at each time point. + % What makes more sense is to average the power during fixation and + % then take the average of each condition in time windows and + % reference it to the fixation average power. Same for srcavgdics + + %{ + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'baseline' , {basePeriod{iLM} basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM} tfavgTimes{iLM}},... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %} + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'timeperiods' , { srcTimesCoarseComp{iLM} srcTimesCoarseCompFIX{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseCompFIX{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseCompFIX{iLM}},... + 'timedef' , 'concat',... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %-------------------------------com------------------------------ + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + %==================================================================== + end + + end + + + + +end + +%============================================================================ +%============================================================================ +%============================================================================ +contrastList=cntrst; +end +%************************************************************************** +% ******** END OF MAIN FUNCTION +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** + +function[selection,description]=trialsplit_Motort(trialinfo, memberTypes,prevBlockMemberTypes) +% INPUTS +% trialinfo : matrix with information about each trial +% memberTypes=[1 2 3 4 5 6]; % Param A: 1.Left Hand 2.Left Foot 3.Tongue 4.Right Hand 5.Right Foot 6.Fixation +% prevBlockMemberTypes=[1 2 3 4 5 6]; % Param B: 1.Left Hand 2.Left Foot 3.Tongue 4.Right Hand 5.Right Foot 6.Fixation + + + +%============================================================== +%======== BELOW ARE THE COLUMNS DESCRIPTION for trialinfo matrix produced +% by the trial function. +% MOTOR TRIAL INFO +%Columns: +%------------------- +% 1. Block Index +% 2. Block Stim Code +% 3. Trial Index in Block (This is derived by finding the flash cross onset just before the EMG onset) +% 4. Trial Onset EMG Sample +% 5. prev. Block Stim Code +% If trials are cut based on EMG then there is one more extra column +% 6. Time from EMG onset to the previous Flashing Cross + + + + +condcfg=[]; +condcfg.memberTypes=[1 2 3 4 5 6]; % Param A: 1.Left Hand 2.Left Foot 3.Tongue 4.Right Hand 5.Right Foot 6.Fixation +condcfg.prevBlockMemberTypes=[1 2 3 4 5 6]; % Param B: 1.Left Hand 2.Left Foot 3.Tongue 4.Right Hand 5.Right Foot 6.Fixation + + +descript=[]; +descript.memberTypes ={ 'Left_Hand' 'Left_Foot' 'Tongue' 'Right Hand' 'Right Foot' 'Fixation'}; +descript.prevBlockMemberTypes ={ 'Left_Hand' 'Left_Foot' 'Tongue' 'Right Hand' 'Right Foot' 'Fixation'}; + +if ~isempty(memberTypes), condcfg.memberTypes=memberTypes; end +if ~isempty(prevBlockMemberTypes), condcfg.prevBlockMemberTypes=prevBlockMemberTypes; end + + +isin_memberTypes=ismember(trialinfo(:,2),condcfg.memberTypes); +isin_prevBlockMemberTypes=ismember(trialinfo(:,5),condcfg.prevBlockMemberTypes); + +%{ +tmpBlockSeq=trialinfo(:,1); +tmpBlockSeqIndx=unique(tmpBlockSeq(ismember(trialinfo(:,2),condcfg.prevBlockMemberTypes),1)); +tmpBlockSeqIndx=tmpBlockSeqIndx+1; +isin_prevBlockMemberTypes=ismember(tmpBlockSeq,tmpBlockSeqIndx); + +%} +isInCase=(isin_memberTypes&isin_prevBlockMemberTypes); +isInCaseCurrent=isin_memberTypes; +selection=find(isInCase); + +if isequal(condcfg.prevBlockMemberTypes,[1 2 3 4 5 6]) + includeFirstOfAll=1; +else + includeFirstOfAll=0; +end + +if includeFirstOfAll + if (sum(selection==1)==0)&(isInCaseCurrent(1)==1), + selection=[1; selection]; + end +end + +description=[]; + +if ~isequal(condcfg.memberTypes,[1 2 3 4 5]) + description=[description, 'memberTypes: ']; + for iTmp=1:length(condcfg.memberTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.memberTypes{condcfg.memberTypes(iTmp)}]; + end + description=[description,'\n']; +end + + +if ~isequal(condcfg.prevBlockMemberTypes,[1 2 3 4 5 6]) + description=[description, 'prevBlockMemberTypes: ']; + for iTmp=1:length(condcfg.prevBlockMemberTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.prevBlockMemberTypes{condcfg.prevBlockMemberTypes(iTmp)+1}]; + end + description=[description,'\n']; +end + + +if isempty(description), + description='allstimuli'; +end + +%========================================== +end + + + +function [contr]=newcntrst(varargin) +% This subfunction creates a default cntrst function + +contr =[]; +contr.pipeline = ft_getopt(varargin,'pipeline',[]); +%pipelines={'eravg','tfavg','srcavglcmv','srcavgdics','conne'}; +%---------------------------------------------- +contr.lockmode = ft_getopt(varargin,'lockmode',{[]}); +%for WM datagroups={'TRLIM','TRLRESP'}; +%---------------------------------------------- +contr.mnemtrl = ft_getopt(varargin,'mnemtrl', {[]}); +% Here go mnemonics for each trial selection i.e. {'0B'} +%---------------------------------------------- +contr.freqband = ft_getopt(varargin,'freqband',[]); +% freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; +%---------------------------------------------- +contr.freq = ft_getopt(varargin,'freq',[]); +% frequencies to be analysed . Currently only used for tfavg pipeline; +%---------------------------------------------- +contr.operation = ft_getopt(varargin,'operation',[]); +% 'diff','rel', or 'relch' This is used when 2 conditions are compared +%---------------------------------------------- +contr.timeperiods = ft_getopt(varargin,'timeperiods',{[]}); +% This defined the times to be used. If 'all' then the result is +% computed for each time point. For each condition the rows indicate +% The time points for which the analysis is +% performed. If a second column is also +% present then the first column is the +% lower and the second the upper time +% limits within which the analysis should +% be performed. i.e. {[-1 -0.5 +% -0.5 0 +% 0 0.5]} +% Means that the analysis should be +% performed for these 3 time windows +% How data is integrated in each window is +% defined by the .timedef field of the +% cntrst +%---------------------------------------------- +contr.timedef = ft_getopt(varargin,'timedef',[]); +% This how data with a time window should be integrated +% It can be 'avg' or 'concat'. If 'avg' then +% the data within a window is averaged +% before computing the result. If 'concat' +% then all the points within the window are +% used for the computation +%---------------------------------------------- +contr.baseline = ft_getopt(varargin,'baseline',{[]}); +% This defines the time period to be used as baseline +%---------------------------------------------- +contr.baselinetype= ft_getopt(varargin,'baselinetype',[]); +% It can be 'diff','rel' or 'relch' and defines how +% the baseline will be used on the rest of the data +% When 2 conditions are compared this +% defines how baseline has been used in +% each condition. In the case of 2 +% conditions in source space this defines +% how baseline has been used in sensor +% space to derive the inverse solution. +%---------------------------------------------- +contr.connemetric = ft_getopt(varargin,'connemetric',[]); +% conneCases={'coh','plv','imcoh','psi','powc','orthopowc'}; % Next to be implemented ,'xpowc','xpowphase','bigranger' +%---------------------------------------------- +contr.invfiltertype = ft_getopt(varargin,'invfiltertype',[]); +% It can be 'com' or 'ind'. This is used when 2 conditions are compared in source space. If 'com' then a common filter +% is derived from both conditions. .If 'ind' the a filter is derived for each condition +%---------------------------------------------- +contr.selection = ft_getopt(varargin,'selection',[]); +% This is a cell with the indices of the trials to be used +%---------------------------------------------- +contr.description = ft_getopt(varargin,'description',[]); +% This is a cell with basic description of the cntrst (needs to be updated) +%---------------------------------------------- +contr.mnemprint = ft_getopt(varargin,'mnemprint',[]); +% This is the mnemonic that will be used to distinguish the cntrst. Used for saving +%======================== ======================================================================== +end + + +function[printMnem]= createcontrmnem(incontr) + +pflags=[]; +%pflags.pipeline = 'PI-'; +pflags.lockmode = 'LM-'; +pflags.freqband = 'FB-'; +pflags.operation = 'OP-'; +pflags.timedef = 'TD-'; +pflags.baselinetype = 'BT-'; +pflags.connemetric = 'CM-'; +pflags.invfiltertype ='IT-'; + + +printMnem=[]; +if isempty(incontr.pipeline) + error('Pipeline must be defined in a constrast') + return; +end + +printMnem=[printMnem,incontr.pipeline]; + +Nlms=length(incontr.lockmode); + +if Nlms==1 + printMnem=[printMnem,'_[',pflags.lockmode,incontr.lockmode{1}]; + printMnem=[printMnem,'-',incontr.mnemtrl{1},']']; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + if ~isempty(incontr.freqband) + printMnem=[printMnem,'_[',pflags.freqband,incontr.freqband,']']; + end + if ~isempty(incontr.operation) + printMnem=[printMnem,'_[',pflags.operation,incontr.operation,']']; + end + if ~isempty(incontr.timedef) + printMnem=[printMnem,'_[',pflags.timedef,incontr.timedef,']']; + end + if ~isempty(incontr.baselinetype) + if ~strcmp(incontr.pipeline,'tfavg') + printMnem=[printMnem,'_[',pflags.baselinetype,incontr.baselinetype,']']; + end + end + if ~isempty(incontr.connemetric) + printMnem=[printMnem,'_[',pflags.connemetric,incontr.connemetric,']']; + end + if ~isempty(incontr.invfiltertype) + printMnem=[printMnem,'_[',pflags.invfiltertype,incontr.invfiltertype,']']; + end +elseif Nlms==2 + printMnem=[printMnem,'_[',pflags.lockmode,incontr.lockmode{1}]; + printMnem=[printMnem,'-',incontr.mnemtrl{1}]; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + printMnem=[printMnem,'-versus']; + %printMnem=[printMnem,pflags.lockmode,incontr.lockmode{2}]; This was removed as + %only contrasts from one + %datagroup are + %supported + printMnem=[printMnem,'-',incontr.mnemtrl{2},']']; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + if ~isempty(incontr.freqband) + printMnem=[printMnem,'_[',pflags.freqband,incontr.freqband,']']; + end + if ~isempty(incontr.operation) + printMnem=[printMnem,'_[',pflags.operation,incontr.operation,']']; + end + if ~isempty(incontr.timedef) + printMnem=[printMnem,'_[',pflags.timedef,incontr.timedef,']']; + end + if ~isempty(incontr.baselinetype) + if ~strcmp(incontr.pipeline,'tfavg') + printMnem=[printMnem,'_[',pflags.baselinetype,incontr.baselinetype,']']; + end + end + if ~isempty(incontr.invfiltertype) + printMnem=[printMnem,'_[',pflags.invfiltertype,incontr.invfiltertype,']']; + end + if ~isempty(incontr.connemetric) + printMnem=[printMnem,'_[',pflags.connemetric,incontr.connemetric,']']; + end + +else + error('1 or 2 different data Lock Modes are supported'); + return; +end +%================================================================= + +end + + + diff --git a/trial_functions/contrast_StoryM.m b/trial_functions/contrast_StoryM.m new file mode 100644 index 0000000..e74b3c7 --- /dev/null +++ b/trial_functions/contrast_StoryM.m @@ -0,0 +1,29 @@ +function[contrastList]=contrast_StoryM(trialinfo) + +% This function produces a default list of contrasts for the Story Math +% paradigm. It uses the 'trialinfo' input matrix which contains all the +% trial information for a SINGLE run (FIXME:append runs). +% Each item in the list is a structure with fields +% .mnemonic % encoded mnemonic +% .description= % cell array with detailed description of the contrast. +% .selection - indices of contrast trials from input trialinfo. +% .operation : type of comparison . can be 'average difference' or 'ratio' + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +contrastList=contrast_StoryM_Basic(trialinfo); diff --git a/trial_functions/contrast_StoryM_Basic.m b/trial_functions/contrast_StoryM_Basic.m new file mode 100644 index 0000000..bc6f9dc --- /dev/null +++ b/trial_functions/contrast_StoryM_Basic.m @@ -0,0 +1,1291 @@ +function[contrastList]=contrast_StoryM_Basic(trlInfo) + +% This function produces a default list of contrasts for the Motor +% paradigm. It uses the 'trialinfo' input matrix which contains all the +% trial information for a SINGLE run (FIXME:append runs). +% Each item in the list is a structure with fields +% .mnemonic % encoded mnemonic +% .description= % cell array with detailed description of the contrast. +% .selection - indices of contrast trials from input trialinfo. +% .operation : type of comparison . can be 'average difference' or 'ratio' + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +lockNames=trlInfo.lockNames; +lockTrl=trlInfo.lockTrl; + +if length(lockNames)~=length(lockTrl) + error(['The number of groups does not match the number of trialinfo matrices']) +end + +% INDIVIDUAL CONDITIONS - extracting condition trial indices and mnemonic labels - + + +%============================================================================== +%--- LOCKED ON EVENTS - ONSET OF SENSTENCE, ONSET OF NUMBER in question or options, ONSET OF calculation WORD , ONSET OF OPTION WORD ------------------------------- +% Fixed size trials + +iTEV=find(strcmp(lockNames,'TEV')); +trialinfo=lockTrl{iTEV}; +datagroupflag=1; + +% INDIVIDUAL CONDITIONS - extracting condition trial indices and mnemonic labels - +%--Case - Onset of Sentence in Story +%- c21== 10 +[seltev_storsentnon,labtev_storsentnon] = trialsplit_StoryM(trialinfo,datagroupflag, [10] , [] , []); +%--Case - Onset of Sentence in Math +%- c21 ==20 and choose the first one in the unit +[seltev_mathsentnon,labtev_mathsentnon] = trialsplit_StoryM(trialinfo,datagroupflag, [20] , 1 , []); +%--Case - Onset of Numbers in Questions +%- c21 ==20 +[seltev_mathnumque,labtev_mathnumque] = trialsplit_StoryM(trialinfo,datagroupflag, [20] , [] , []); +%--Case - Onset of Late Numbers in Questions (It seems that there always 4 numbers in quetions. If this is the case pick the last 2 as late and first two as early) +%- c21 ==20 and choose the ones for which the subunit number in unit is +%higher than half +[seltev_mathnumquelate,labtev_mathnumquelate] = trialsplit_StoryM(trialinfo,datagroupflag, [20] , 3 , []); +%--Case - Onset of Early Numbers in Questions (It seems that there always 4 numbers in questions. If this is the case pick the last 2 as late and first two as early) +%- c21 ==20 and choose the ones for which the subunit number in unit is +%lower than half +[seltev_mathnumqueearly,labtev_mathnumqueearly] = trialsplit_StoryM(trialinfo,datagroupflag, [20] , 2 , []); +%--Case - Onset of Numbers in Options +% -c21==23 or 25 +[seltev_mathnumopt,labtev_mathnumopt] = trialsplit_StoryM(trialinfo,datagroupflag, [23 25] , [] , []); +%--Case - Onset of Correct Numbers in Options +% -c21==23 or 25 and fuse with c14 which shows which option is the correct +% one +[seltev_mathnumoptcor,labtev_mathnumoptcor] = trialsplit_StoryM(trialinfo,datagroupflag, [23 25] , [] , 1); +%--Case - Onset of Wrong Numbers in Options +% -c21==23 or 25 and fuse with c14 which shows which option is the correct +% one +[seltev_mathnumoptwro,labtev_mathnumoptwro] = trialsplit_StoryM(trialinfo,datagroupflag, [23 25] , [] , 0); +%--Case - Onset of Correct Story Answer in Options +% -c21==13 or 15 and fuse with c14 which shows which option is the correct +% one +[seltev_storoptcor,labtev_storoptcor] = trialsplit_StoryM(trialinfo,datagroupflag, [13 15] , [] , 1); +%--Case - Onset of Wrong Story Answer in Options +% -c21==13 or 15 and fuse with c14 which shows which option is the correct +% one +[seltev_storoptwro,labtev_storoptwro] = trialsplit_StoryM(trialinfo,datagroupflag, [13 15] , [] , 0); +%--Case - Onset of Math operations words in Questions (in order to compare with numbers in question the first number should not be included so that the number of trials match - hmmmm) +%-c21 = 21 +[seltev_mathoper,labtev_mathoper] = trialsplit_StoryM(trialinfo,datagroupflag, 21 , [] , []); + + + + +%--- LOCKED ON RESPONSE +% fixed size trials +iTRESP=find(strcmp(lockNames,'TRESP')); +trialinfo=lockTrl{iTRESP}; +datagroupflag=2; +%--Case - All responses +%- check c21 to get only trials that there was a response +[seltresp_all,labtresp_all] = trialsplit_StoryM(trialinfo,datagroupflag, [] , [] , []); +%--Case - responses in stories +%- check c21 to get only trials that there was a response and then get +%- c2==1 +[seltresp_stor,labtresp_stor] = trialsplit_StoryM(trialinfo,datagroupflag, 1 , [] , []); +%--Case - responses in maths +%- check c21 to get only trials that there was a response and then get +%- c2==2 +[seltresp_math,labtresp_math] = trialsplit_StoryM(trialinfo,datagroupflag, 2 , [] , []); + + +%--- LOCKED ON SENTENCE (STORY SENTENCE or MATH SENTENCE excluding option ) +% Variable size trials +iBSENT=find(strcmp(lockNames,'BSENT')); +trialinfo=lockTrl{iBSENT}; +%--Case - all story sentences +%-c2==1 +[selbsent_stor,labbsent_stor] = trialsplit_StoryM(trialinfo,datagroupflag, 1 , [] , []); +%--Case - all story sentences late (the late half of sentences in a story) +%-c2==1 and use c8 and c11 in order to select the later half sentences in +%each story. +[selbsent_storlate,labbsent_storlate] = trialsplit_StoryM(trialinfo,datagroupflag, 1 , 3 , []); +%--Case - all story sentences early (the early half of sentences in a story) +%-c2==1 and use c8 and c11 in order to select the earlier half sentences in +%each story. +[selbsent_storearly,labbsent_storearly] = trialsplit_StoryM(trialinfo,datagroupflag, 1 , 2 , []); +%--Case - all math sentences +%-c2==2 +[selbsent_math,labbsent_math] = trialsplit_StoryM(trialinfo,datagroupflag, 2 , [] , []); + + +%--- LOCKED ON UNIT (STORY BLOCK or MATH BLOCK including option ) +% Variable size trials +iBU=find(strcmp(lockNames,'BU')); +trialinfo=lockTrl{iBU}; +%--Case - Story Units +%-c2==1 +[selbu_stor,labbu_stor] = trialsplit_StoryM(trialinfo,datagroupflag, 1 , [] , []); +%--Case - Math Units +%-c2==2 +[selbu_math,labbu_math] = trialsplit_StoryM(trialinfo,datagroupflag, 2 , [] , []); + + + +%============================================================ +%============================================================ +%============================================================ +%============================================================ +%============================================================ + +LockModes={'TEV' % Lock modes represent different pools of trial data. + 'TRESP' + 'BSENT' + 'BU'}; + +Nlm=length(LockModes); + +%--- LockMode 'TEV' +tfavgTimes{1}=[-1:0.025:3.5]'; +srcTimesFine{1}=[-1:0.025:3.5]'; +srcTimesCoarseSing{1}=[0 0.5 + 0.5 1 + 1 1.5 + 1.5 2]; +srcTimesCoarseComp{1}=[-0.5 0 + 0 0.5 + 0.5 1 + 1 1.5 + 1.5 2]; +basePeriod{1}=[-0.5 0]; + +%--- LockMode 'TRESP' +tfavgTimes{2}=[-1.25:0.025:1.25]'; +srcTimesFine{2}=[-1.25:0.025:1.25]'; +srcTimesCoarseSing{2}=[-0.25 0.25 + 0.25 0.75 + 0.75 1.25]; +srcTimesCoarseComp{2}=[-0.75 -0.25 + -0.25 0.25 + 0.25 0.75 + 0.75 1.25]; +basePeriod{2}=[-0.75 -0.25]; + +%--- LockMode 'BSENT' +tfavgTimes{3}=[]; % No contrast will be computed at sensor level +srcTimesFine{3}=[]; % As there is variable trial length no fine time resolution will be computed in source level +srcTimesCoarseSing{3}=[]; % Same as above +srcTimesCoarseComp{3}=[]; % In the comparisons the metrics will be computed from the entire trial length +basePeriod{3}=[]; % No baseline required here + +%--- LockMode 'BU' +tfavgTimes{4}=[]; % No contrast will be computed at sensor level +srcTimesFine{4}=[]; % As there is variable trial length no fine time resolution will be computed in source level +srcTimesCoarseSing{4}=[]; % Same as above +srcTimesCoarseComp{4}=[]; % In the comparisons the metrics will be computed from the entire trial length +basePeriod{4}=[]; % No baseline required here +%======================================================================= +%======================================================================= +tfavgFreqs=[1:100]; +freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; + + + +%{ +SENTENCE IN STORY vs ONSET OF SENTENCE IN MATH +ONSET OF LATE NUMBERS IN QUESTIONS vs ONSET OF EARLY NUMBER IS QUESTIONS +ONSET OF CORRECT NUMBERS IN OPTIONS vs ONSET OF WRONG NUMBERS IN OPTIONS +ONSET OF CORRECT ANSWERS IN STORY OPTIONS vs ONSET OF WRONG STORY ANSWERS +ONSET OF NUMBER WORDS vs ONSET OF OPERAND WORDS IN QUESTIONS + +TRESP +ONSET OF ALL REPONSES + +BSENT: +LATE STORY SENTENCES VS EARLY STORY SENTENCES + +BU: +STORIES vs MATHS + + + +%} +cntrst=[]; +%===========CONTRASTS FOR TEV +% SINGLE CASES ==================== +%========================================================================= +tmpSingCases={'mathnumque','storsentnon','mathsentnon','mathnumopt','mathoper'}; +iLM=1; +lmstr=lower(LockModes{iLM}); + +for iCase=1:length(tmpSingCases) + + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM}},... + 'baseline' , {basePeriod{iLM}},... % In tfavg the Baseline is used ONLY FOR PLOTTING + 'baselinetype' , 'diff',... % In tfavg the Baseline is used ONLY FOR PLOTTING + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric ' , 'coh',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseSing{iLM}},... + 'timedef' , 'concat',... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + end +end + +%==================================================================== +% COMPARISONS +tmpCompCases={ 'storsentnon' 'mathsentnon' + 'mathnumquelate' 'mathnumqueearly' + 'mathnumoptcor' 'mathnumoptwro' + 'storoptcor' 'storoptwro' + 'mathnumque' 'mathoper'}; + + +%============================================================ +%============================================================ +%========CONSTRASTS FOR COMPARISONS BETWEEN CONDITIONS ====== +%============================================================ + +for iGroup=1:size(tmpCompCases,1) + %------------------------------------------------------------- + + + + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'baseline' , {basePeriod{iLM} basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM} tfavgTimes{iLM}},... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'timeperiods' , { srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'timedef' , 'concat',... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %-------------------------------com------------------------------ + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + %==================================================================== + end + +end + +%==================================================================== +%================================================================== +%==================================================================== +%================================================================== +%==================================================================== +%================================================================== +%===========CONTRASTS FOR TRESP +% SINGLE CASES ==================== +%========================================================================= + +iLM=2; +lmstr=lower(LockModes{iLM}); +tmpSingCases={'all'}; + +for iCase=1:length(tmpSingCases) + + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric ' , 'coh',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseSing{iLM}},... + 'timedef' , 'concat',... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + end +end + +%==================================================================== +%==================================================================== +%================================================================== +%==================================================================== +%================================================================== +%==================================================================== +%================================================================== + +%==================================================================== +%- datagroup BSENT +%---- No single conditions +%---- only comparisons +iLM=3; +lmstr=lower(LockModes{iLM}); + +% COMPARISONS +tmpCompCases={ 'storlate' 'storearly'}; + + +%============================================================ +%============================================================ +%========CONSTRASTS FOR COMPARISONS BETWEEN CONDITIONS ====== +%============================================================ + +for iGroup=1:size(tmpCompCases,1) + %------------------------------------------------------------- + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'timeperiods' , { srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'timedef' , 'concat',... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %-------------------------------com------------------------------ + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + %==================================================================== + end + +end + +%==================================================================== +%================================================================== +%==================================================================== +%================================================================== +%==================================================================== +%================================================================== + +%==================================================================== +%- datagroup BU +%---- No single conditions +%---- only comparisons +iLM=4; +lmstr=lower(LockModes{iLM}); + +% COMPARISONS +tmpCompCases={'stor' 'math'}; + + +%============================================================ +%============================================================ +%========CONSTRASTS FOR COMPARISONS BETWEEN CONDITIONS ====== +%============================================================ + +for iGroup=1:size(tmpCompCases,1) + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'timeperiods' , { srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM} srcTimesCoarseComp{iLM}},... + 'timedef' , 'concat',... + 'invfiltertype', 'ind',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %-------------------------------com------------------------------ + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + %==================================================================== + end + +end + +%================================================================== + + + + + + + +contrastList=cntrst; +end +%************************************************************************** +% ******** END OF MAIN FUNCTION +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** + +function[selection,description]=trialsplit_StoryM(trialinfo,datagroupflag, evType,orderType,corrOptType) +% INPUTS +% trialinfo : matrix with information about each trial +% datagroupflag : 1: TEV locked on the most detailed events . This is +% sentences for the Story , NUmbers and operator words +% for maths , option 1 - FIXED length trials +% , OR word, opton 2. +% 2: TRESP: locked on the response - FIXED length trials +% 3: BSENT: each trial is a sentence. Either a sentence of a story of a Math question sentence - Variable trial length +% 4: BU: each trials is a unit , meaning one story +% (including option) or one math problem(including option) +% +%evType = For TEV Column 21 of trialinfo. See below for description. For +%the rest of datagroups is column 2. +%orderType = 1: The first one, 2: The early ones 3 : The late ones +%corrType = 0: Wrong Options 1: Correct Options +%============================================================== +%======== BELOW ARE THE COLUMNS DESCRIPTIONS +% TEV +%--------------------------------------------- +%detailed trlAllEventInfo +% Columns: +%---------------------------------------------- +% 1. Block Number within Run +% 2. Unit Type : 1.Story 2.Math +% 3. Unit Number within Run +% 4. Total Number of units (N of Stories or N of Math problems ) in same Run +% 5. Unit Number within Block +% 6. Total Number of units (N of Stories or N of Math problems ) in same Block +% 7. Attribute1: For story this is the story number. For Math is the +% difficulty level +% 8. Unit Narration interval Start Sample - Start with the onset of the +% first word trigger or the beginning of the first sentence +% 9. Unit Narration interval End Sample +% 10. N subunits within Narration interval +% 11. Unit Option interval Start Sample +% 12. Unit Option interval End Sample +% 13. N subunits within Option interval +% 14 Correct Option- 1 or 2 +% 15 Unit Response interval start sample +% 16 Unit Response interval end sample +% 17 Unit Response sample +% 18 is Response Correct +% 19 is Response Early +% 20 Event Sample +% 21 Event Type - 20.Math Narration number word, 21.Math Narration operand +% word, 22.Math Option Intro Word 23.Math Option 1 Word 24. Math Option +% OR Word 25.Math Option 2 Word +% 10.Story Sentence, 12.Story Option Intro 13.Story Option 1 Word 14. Story Option +% OR Word 15.Story Option 2 Word +% 22. Narration Event Number in Narration interval (Applies only to Sentence or number or operation in Narration interval) +%============================================================== +% TRESP: +%----------------------------- +% trlUnitInfo +% Columns: +%---------------------------------------------- +% 1. Block Number within Run +% 2. Unit Type : 1.Story 2.Math +% 3. Unit Number within Run +% 4. Total Number of units (N of Stories or N of Math problems ) in same Run +% 5. Unit Number within Block +% 6. Total Number of units (N of Stories or N of Math problems ) in same Block +% 7. Attribute1: For story this is the story number. For Math is the +% difficulty level +% 8. Unit Narration interval Start Sample - Start with the onset of the +% first word trigger or the beginning of the first sentence +% 9. Unit Narration interval End Sample +% 10. N subunits within Narration interval +% 11. Unit Option interval Start Sample +% 12. Unit Option interval End Sample +% 13. N subunits within Option interval +% 14. Option Intro Sample - "equals" or "That was about" +% 15. Option1 onset sample +% 16. OR onset sample +% 17. Option2 onset sample +% 18. Correct Option- 1 or 2 +% 19. Unit Response interval start sample +% 20. Unit Response interval end sample +% 21. Unit Response sample +% 22. is Response Correct +% 23. is Response Early +%===================================================================== +% BSENT +% trlSentInfo +% Each trials corresponds to a Narration Sentence , either in story or in +% math (option blocks not included) +% Columns: +%---------------------------------------------- +% 1. Block Number within Run +% 2. Unit Type : 1.Story 2.Math +% 3. Unit Number within Run +% 4. Total Number of units (N of Stories or N of Math problems ) in same Run +% 5. Unit Number within Block +% 6. Total Number of units (N of Stories or N of Math problems ) in same Block +% 7. Attribute1: For story this is the story number. For Math is the +% difficulty level +% 8. N sentences within Narration interval (always one for math as in math one narration interval corresponds to one math sentence) +% 9. is Response Correct (For Story this refers to the response at the very end of the sentence) +% 10. is Response Early +% 11. Narration Sentence Number in Narration interval (For math always equal to one) +% 12. Narration Sentence Start Sample +% 13. Narration Sentence End Sample +%----------------------------------------- +if datagroupflag==1 %TEV + condcfg=[]; + condcfg.evTypes=[10 20 13 15 23 25 21]; + condcfg.orderTypes=[1 2 3]; + condcfg.corrTypes=[0 1]; + + descript=[]; + descript.evTypes={'Story_Sentence_On' , 'Math_Number_InQuestion' ,'Story_Option_1' , 'Story_Option_2' , 'Math_Option_1' , 'Math_Option_2', 'Math_Operand'}; + descript.orderTypes={'First' , 'Early' , 'Late'}; + descript.corrTypes={'Wrong','Early','Late'}; + + + tmptrialinfo=trialinfo; + tmpDescr=[]; + + + + isin_evType=ismember(tmptrialinfo(:,21),evType); + totIndx=find(isin_evType); + tmptrialinfo=tmptrialinfo(isin_evType,:); + if length(evType)==1, + tmpIndx=ismember(condcfg.evTypes,evType); + tmpDescr= descript.evTypes{tmpIndx}; + else + if (evType==[13 15]) + tmpDescr=['Story_Options']; + if corrOptType==0 + tmpIndx1=find((tmptrialinfo(:,14)==1)&(tmptrialinfo(:,21)==15)); + tmpIndx2=find((tmptrialinfo(:,14)==2)&(tmptrialinfo(:,21)==13)); + tmpIndx=[tmpIndx1 ; tmpIndx2]; + tmpIndx=unique(tmpIndx); % Check if there are duplicate entries. There shouldnt + totIndx=totIndx(tmpIndx); + tmptrialinfo=tmptrialinfo(tmpIndx,:); + tmpDescr=[tmpDescr,'_Wrong']; + elseif corrOptType==1 + tmpIndx1=find((tmptrialinfo(:,14)==1)&(tmptrialinfo(:,21)==13)); + tmpIndx2=find((tmptrialinfo(:,14)==2)&(tmptrialinfo(:,21)==15)); + tmpIndx=[tmpIndx1 ; tmpIndx2]; + tmpIndx=unique(tmpIndx); % Check if there are duplicate entries. There shouldnt + totIndx=totIndx(tmpIndx); + tmptrialinfo=tmptrialinfo(tmpIndx,:); + tmpDescr=[tmpDescr,'_Correct']; + end + + + elseif (evType==[23 25]) + tmpDescr=['Math_Options']; + if corrOptType==0 + tmpIndx1=find((tmptrialinfo(:,14)==1)&(tmptrialinfo(:,21)==25)); + tmpIndx2=find((tmptrialinfo(:,14)==2)&(tmptrialinfo(:,21)==23)); + tmpIndx=[tmpIndx1 ; tmpIndx2]; + tmpIndx=unique(tmpIndx); % Check if there are duplicate entries. There shouldnt + totIndx=totIndx(tmpIndx); + tmptrialinfo=tmptrialinfo(tmpIndx,:); + tmpDescr=[tmpDescr,'_Wrong']; + elseif corrOptType==1 + tmpIndx1=find((tmptrialinfo(:,14)==1)&(tmptrialinfo(:,21)==23)); + tmpIndx2=find((tmptrialinfo(:,14)==2)&(tmptrialinfo(:,21)==25)); + tmpIndx=[tmpIndx1 ; tmpIndx2]; + tmpIndx=unique(tmpIndx); % Check if there are duplicate entries. There shouldnt + totIndx=totIndx(tmpIndx); + tmptrialinfo=tmptrialinfo(tmpIndx,:); + tmpDescr=[tmpDescr,'_Correct']; + end + + + elseif (evType==[13 15 23 25]); + tmpDescr=['Options']; + if corrOptType==0 + tmpIndx1=find((tmptrialinfo(:,14)==1)&ismember(tmptrialinfo(:,21),[15 25])); + tmpIndx2=find((tmptrialinfo(:,14)==2)&ismember(tmptrialinfo(:,21),[13 23])); + tmpIndx=[tmpIndx1 ; tmpIndx2]; + tmpIndx=unique(tmpIndx); % Check if there are duplicate entries. There shouldnt + totIndx=totIndx(tmpIndx); + tmptrialinfo=tmptrialinfo(tmpIndx,:); + tmpDescr=[tmpDescr,'_Wrong']; + elseif corrOptType==1 + tmpIndx1=find((tmptrialinfo(:,14)==1)&ismember(tmptrialinfo(:,21),[13 23])); + tmpIndx2=find((tmptrialinfo(:,14)==2)&ismember(tmptrialinfo(:,21),[15 25])); + tmpIndx=[tmpIndx1 ; tmpIndx2]; + tmpIndx=unique(tmpIndx); % Check if there are duplicate entries. There shouldnt + totIndx=totIndx(tmpIndx); + tmptrialinfo=tmptrialinfo(tmpIndx,:); + tmpDescr=[tmpDescr,'_Correct']; + end + + end + + end + + if ~isempty(orderType) % It only + if orderType==1, + isin_orderType=(tmptrialinfo(:,22)==1); + tmpDescr=[tmpDescr,'_First']; + elseif orderType==2, + isin_orderType=(tmptrialinfo(:,22)<=floor(tmptrialinfo(:,10)./2)); % Early + tmpDescr=[tmpDescr,'_Early']; + elseif orderType==3, + isin_orderType=(tmptrialinfo(:,22)>floor(tmptrialinfo(:,10)./2)); % Late + tmpDescr=[tmpDescr,'_Late']; + end + tmptrialinfo=tmptrialinfo(isin_orderType,:); + totIndx=totIndx(isin_orderType); + end + + selection=totIndx; + description=tmpDescr; + + + +elseif datagroupflag==2 %TRESP + + tmptrialinfo=trialinfo; + tmpDescr=[]; + totIndx=[]; + + if ~isempty(evType) + isin_evType=ismember(tmptrialinfo(:,2),evType); + totIndx=find(isin_evType); + tmptrialinfo=tmptrialinfo(isin_evType,:); + + if evType==1, + tmpDescr='Story'; + elseif evType==2, + tmpDescr='Math'; + end + + else + totIndx=[1:size(tmptrialinfo,1)]; + end + %----- + if isempty(tmpDescr) + tmpDescr='all'; + end + + + selection=totIndx; + description=tmpDescr; + + + +elseif datagroupflag==3 %BSENT + + tmptrialinfo=trialinfo; + tmpDescr=[]; + totIndx=[]; + + if ~isempty(evType) + isin_evType=ismember(tmptrialinfo(:,2),evType); + totIndx=find(isin_evType); + tmptrialinfo=tmptrialinfo(isin_evType,:); + + if evType==1, + tmpDescr='Story'; + elseif evType==2, + tmpDescr='Math'; + end + %------------------------------------ + if ~isempty(orderType) + if orderType==2, %early + isin_orderType=(tmptrialinfo(:,11)<=floor(tmptrialinfo(:,8)./2)); % Early + tmpDescr=[tmpDescr,'_Early']; + elseif orderType==3, % late + isin_orderType=(tmptrialinfo(:,11)>floor(tmptrialinfo(:,8)./2)); % Late + tmpDescr=[tmpDescr,'_Late']; + end + tmptrialinfo=tmptrialinfo(isin_orderType,:); + totIndx=totIndx(isin_orderType); + end + %------------------------------------ + else + totIndx=[1:size(tmptrialinfo,1)]; + end + %----- + if isempty(tmpDescr) + tmpDescr='all'; + end + + + selection=totIndx; + description=tmpDescr; + + + +elseif datagroupflag==4 %BU + + tmptrialinfo=trialinfo; + tmpDescr=[]; + totIndx=[]; + + if ~isempty(evType) + isin_evType=ismember(tmptrialinfo(:,2),evType); + totIndx=find(isin_evType); + tmptrialinfo=tmptrialinfo(isin_evType,:); + + if evType==1, + tmpDescr='Story'; + elseif evType==2, + tmpDescr='Math'; + end + + else + totIndx=[1:size(tmptrialinfo,1)]; + end + %----- + if isempty(tmpDescr) + tmpDescr='all'; + end + + + selection=totIndx; + description=tmpDescr; + +end + + + +end +%========================================== +%========================================== +%========================================== +%========================================== +%========================================== +%========================================== + + + +function [contr]=newcntrst(varargin) +% This subfunction creates a default cntrst function + +contr =[]; +contr.pipeline = ft_getopt(varargin,'pipeline',[]); +%pipelines={'eravg','tfavg','srcavglcmv','srcavgdics','conne'}; +%---------------------------------------------- +contr.lockmode = ft_getopt(varargin,'lockmode',{[]}); +%for WM datagroups={'TRLIM','TRLRESP'}; +%---------------------------------------------- +contr.mnemtrl = ft_getopt(varargin,'mnemtrl',{[]}); +% Here go mnemonics for each trial selection i.e. {'0B'} +%---------------------------------------------- +contr.freqband = ft_getopt(varargin,'freqband',[]); +% freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; +%---------------------------------------------- +contr.freq = ft_getopt(varargin,'freq',[]); +% frequencies to be analysed . Currently only used for tfavg pipeline; +%---------------------------------------------- +contr.operation = ft_getopt(varargin,'operation',[]); +% 'diff','rel', or 'relch' This is used when 2 conditions are compared +%---------------------------------------------- +contr.timeperiods = ft_getopt(varargin,'timeperiods',{[]}); +% This defined the times to be used. If 'all' then the result is +% computed for each time point. For each condition the rows indicate +% The time points for which the analysis is +% performed. If a second column is also +% present then the first column is the +% lower and the second the upper time +% limits within which the analysis should +% be performed. i.e. {[-1 -0.5 +% -0.5 0 +% 0 0.5]} +% Means that the analysis should be +% performed for these 3 time windows +% How data is integrated in each window is +% defined by the .timedef field of the +% cntrst +%---------------------------------------------- +contr.timedef = ft_getopt(varargin,'timedef',[]); +% This how data with a time window should be integrated +% It can be 'avg' or 'concat'. If 'avg' then +% the data within a window is averaged +% before computing the result. If 'concat' +% then all the points within the window are +% used for the computation +%---------------------------------------------- +contr.baseline = ft_getopt(varargin,'baseline',{[]}); +% This defines the time period to be used as baseline +%---------------------------------------------- +contr.baselinetype= ft_getopt(varargin,'baselinetype',[]); +% It can be 'diff','rel' or 'relch' and defines how +% the baseline will be used on the rest of the data +% When 2 conditions are compared this +% defines how baseline has been used in +% each condition. In the case of 2 +% conditions in source space this defines +% how baseline has been used in sensor +% space to derive the inverse solution. +%---------------------------------------------- +contr.connemetric = ft_getopt(varargin,'connemetric',[]); +% conneCases={'coh','plv','imcoh','psi','powc','orthopowc'}; % Next to be implemented ,'xpowc','xpowphase','bigranger' +%---------------------------------------------- +contr.invfiltertype = ft_getopt(varargin,'invfiltertype',[]); +% It can be 'com' or 'ind'. This is used when 2 conditions are compared in source space. If 'com' then a common filter +% is derived from both conditions. .If 'ind' the a filter is derived for each condition +%---------------------------------------------- +contr.selection = ft_getopt(varargin,'selection',[]); +% This is a cell with the indices of the trials to be used +%---------------------------------------------- +contr.description = ft_getopt(varargin,'description',[]); +% This is a cell with basic description of the cntrst (needs to be updated) +%---------------------------------------------- +contr.mnemprint = ft_getopt(varargin,'mnemprint',[]); +% This is the mnemonic that will be used to distinguish the cntrst. Used for saving +%================================================================================================ +end + + + + +function[printMnem]= createcontrmnem(incontr) + +pflags=[]; +%pflags.pipeline = 'PI-'; +pflags.lockmode = 'LM-'; +pflags.freqband = 'FB-'; +pflags.operation = 'OP-'; +pflags.timedef = 'TD-'; +pflags.baselinetype = 'BT-'; +pflags.connemetric = 'CM-'; +pflags.invfiltertype ='IT-'; + + +printMnem=[]; +if isempty(incontr.pipeline) + error('Pipeline must be defined in a constrast') + return; +end + +printMnem=[printMnem,incontr.pipeline]; + +Nlms=length(incontr.lockmode); + +if Nlms==1 + printMnem=[printMnem,'_[',pflags.lockmode,incontr.lockmode{1}]; + printMnem=[printMnem,'-',incontr.mnemtrl{1},']']; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + if ~isempty(incontr.freqband) + printMnem=[printMnem,'_[',pflags.freqband,incontr.freqband,']']; + end + if ~isempty(incontr.operation) + printMnem=[printMnem,'_[',pflags.operation,incontr.operation,']']; + end + if ~isempty(incontr.timedef) + printMnem=[printMnem,'_[',pflags.timedef,incontr.timedef,']']; + end + if ~isempty(incontr.baselinetype) + if ~strcmp(incontr.pipeline,'tfavg') + printMnem=[printMnem,'_[',pflags.baselinetype,incontr.baselinetype,']']; + end + end + if ~isempty(incontr.connemetric) + printMnem=[printMnem,'_[',pflags.connemetric,incontr.connemetric,']']; + end + if ~isempty(incontr.invfiltertype) + printMnem=[printMnem,'_[',pflags.invfiltertype,incontr.invfiltertype,']']; + end +elseif Nlms==2 + printMnem=[printMnem,'_[',pflags.lockmode,incontr.lockmode{1}]; + printMnem=[printMnem,'-',incontr.mnemtrl{1}]; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + printMnem=[printMnem,'-versus']; + %printMnem=[printMnem,pflags.lockmode,incontr.lockmode{2}]; This was removed as + %only contrasts from one + %datagroup are + %supported + printMnem=[printMnem,'-',incontr.mnemtrl{2},']']; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + if ~isempty(incontr.freqband) + printMnem=[printMnem,'_[',pflags.freqband,incontr.freqband,']']; + end + if ~isempty(incontr.operation) + printMnem=[printMnem,'_[',pflags.operation,incontr.operation,']']; + end + if ~isempty(incontr.timedef) + printMnem=[printMnem,'_[',pflags.timedef,incontr.timedef,']']; + end + if ~isempty(incontr.baselinetype) + if ~strcmp(incontr.pipeline,'tfavg') + printMnem=[printMnem,'_[',pflags.baselinetype,incontr.baselinetype,']']; + end + end + if ~isempty(incontr.invfiltertype) + printMnem=[printMnem,'_[',pflags.invfiltertype,incontr.invfiltertype,']']; + end + if ~isempty(incontr.connemetric) + printMnem=[printMnem,'_[',pflags.connemetric,incontr.connemetric,']']; + end + +else + error('1 or 2 different data Lock Modes are supported'); + return; +end +%================================================================= + +end diff --git a/trial_functions/contrast_Wrkmem.m b/trial_functions/contrast_Wrkmem.m new file mode 100644 index 0000000..2af7455 --- /dev/null +++ b/trial_functions/contrast_Wrkmem.m @@ -0,0 +1,29 @@ +function[contrastList]=contrast_Wrkmem(trialinfo) + +% This function produces a default list of contrasts for the Working Memory +% paradigm. It uses the 'trialinfo' input matrix which contains all the +% trial information for a SINGLE run (FIXME:append runs). +% Each item in the list is a structure with fields +% .mnemonic % encoded mnemonic +% .description= % cell array with detailed description of the contrast. +% .selection - indices of contrast trials from input trialinfo. +% .operation : type of comparison . can be 'average difference' or 'ratio' + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +contrastList=contrast_Wrkmem_Basic(trialinfo); diff --git a/trial_functions/contrast_Wrkmem_Basic.m b/trial_functions/contrast_Wrkmem_Basic.m new file mode 100644 index 0000000..ca1f788 --- /dev/null +++ b/trial_functions/contrast_Wrkmem_Basic.m @@ -0,0 +1,1080 @@ +function[cntrstList]=contrast_Wrkmem_Basic(trlInfo) + +% This function produces a default list of cntrsts for the Working Memory +% paradigm. It uses the 'trialinfo' input matrix which contains all the +% trial information for a SINGLE run (FIXME:append runs). +% Each item in the list is a structure with fields +% .mnemonic % encoded mnemonic +% .description= % cell array with detailed description of the cntrst. +% .selection - indices of cntrst trials from input trialinfo. +% .operation : type of comparison . can be 'average difference' or 'ratio' +% +% List of cntrsts and comparisons for working memory +%------------------------------------------------------------- +%--Case 1 - fixation +%--Case 2 - all images , memory, target types (no settings , default) +%--Case 3 - OBack +%--Case 4 - 2Back +%--Case 5 - faces +%--Case 6 - tools +%--Case 7 - targets +%--Case 8 - non targets and lures +%--Case 9 - 0-Back Targets +%--Case 10 - 0-Back Non-Targets and Lures +%--Case 11 - 2-Back Targets +%--Case 12 - 2-Back Non-Targets and Lures +%--Case 13 - 2-Back faces +%--Case 14 - 0-Back faces +%----- Comparisons +% 15. 0-Back VS 2-Back 'average difference' +% 16. 0-Back VS 2-Back 'average ratio' +% 17. faces VS tools 'average difference' +% 18. faces VS tools 'average ratio' +% 19. targets VS nontargets and lures 'average difference' +% 20. targets VS nontargets and lures 'average ratio' +% 21. 0-Back Targets VS 0-Back Non-Targets and Lures 'average difference' +% 22. 0-Back Targets VS 0-Back Non-Targets and Lures 'average ratio' +% 23. 2-Back Targets VS 2-Back Non-Targets and Lures 'average difference' +% 24. 2-Back Targets VS 2-Back Non-Targets and Lures 'average ratio' +% 25. 2-Back Targets VS 0-Back Targets 'average difference' +% 26. 2-Back Targets VS 0-Back Targets 'average ratio' +% 27. 2-Back faces VS 0-Back faces 'average difference' +% 28. 2-Back faces VS 0-Back faces 'average ratio' + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + + +%=========================================================================== +%{ The main selection of cntrsts is based on the input paramters (which should correspond +% to columns of the trialinfo matrix created by the corresponding +% trialun). Within this function at the bottom there is a subfunction +% called trialsplit_, which is the trial index selection +% engine. This function takes the folloing inputs which should be +% extracted directly from the trialinfo matrix. +%{ +function[selection,mnemonic]=trialsplit_Wrkmem(... + trialinfo,... % arg. 1 + imageTypes,... % arg. 2 + memoryTypes,... % arg. 3 + targetTypes,... % arg. 4 + hasResponded,... % arg. 5 + responseTypes,... % arg. 6 + prevBlockImageTypes,... % arg. 7 + prevBlockMemoryTypes,... % arg. 8 + prevTrialTargetTypes,... % arg. 9 + prevTrialHasResponded,... % arg. 10 + prevTrialResponseTypes) % arg. 11 +%} + + + +% === The following are the main general division of analysis +%freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; +%pipelines={'eravg','tfavg','srcavglcmv','srcavgdics','conne'}; +%conneCases={'coh','plv','imcoh','psi','powc','orthopowc','xpowc','xpowphase','bigranger'}; + +%== The following are the main subsets of data to apply analysis +%datagroups={'TIM','TRESP'}; +%tmpStimCases={'allstim','0B','2B','face','tool','targ','nontarg','lure'}; + + +lockNames=trlInfo.lockNames; +lockTrl=trlInfo.lockTrl; + +if length(lockNames)~=length(lockTrl) + error(['The number of groups does not match the number of trialinfo matrices']); +end + +%============================================================================== +%--- LOCKED ON IMAGE ONSET ------------------------------- + +iTIM=find(strcmp(lockNames,'TIM')); +trialinfo=lockTrl{iTIM}; +%===================================================================================== +% INDIVIDUAL CONDITIONS - extracting condition trial indices and mnemonic labels - +%--Case - fixation +[seltim_fix,labtim_fix] = trialsplit_Wrkmem(trialinfo, 0,[],[],[],[],[],[],[],[],[]); +%--Case - all images , memory, target types (no settings , default) +[seltim_allstim,labtim_allstim] = trialsplit_Wrkmem(trialinfo,[],[],[],[],[],[],[],[],[],[]); +%--Case - OBack +[seltim_0B,labtim_0B] = trialsplit_Wrkmem(trialinfo,[], 1,[],[],[],[],[],[],[],[]); +%--Case - 2Back +[seltim_2B,labtim_2B] = trialsplit_Wrkmem(trialinfo,[], 2,[],[],[],[],[],[],[],[]); +%--Case - faces +[seltim_face,labtim_face] = trialsplit_Wrkmem(trialinfo, 1,[],[],[],[],[],[],[],[],[]); +%--Case - tools +[seltim_tool,labtim_tool] = trialsplit_Wrkmem(trialinfo, 2,[],[],[],[],[],[],[],[],[]); +%--Case - targets +[seltim_targ,labtim_targ] = trialsplit_Wrkmem(trialinfo,[],[], 1,[],[],[],[],[],[],[]); +%--Case - non targets +[seltim_nontarg,labtim_nontarg] = trialsplit_Wrkmem(trialinfo,[],[],[2],[],[],[],[],[],[],[]); +%--Case - lure +[seltim_lure,labtim_lure] = trialsplit_Wrkmem(trialinfo,[],[],[3],[],[],[],[],[],[],[]); +%--Case - non targets and lures +[seltim_nontargandlure,labtim_nontargandlure] = trialsplit_Wrkmem(trialinfo,[],[],[2 3],[],[],[],[],[],[],[]); +%--Case - 0-Back Targets +[seltim_0Btarg,labtim_0Btarg]=trialsplit_Wrkmem(trialinfo,[],1,1,[],[],[],[],[],[],[]); +%--Case - 0-Back Non-Targets and Lures +[seltim_0Bnontargandlure,labtim_0Bnontargandlure]=trialsplit_Wrkmem(trialinfo,[],[1],[2 3],[],[],[],[],[],[],[]); +%--Case 11 - 2-Back Targets +[seltim_2Btarg,labtim_2Btarg]=trialsplit_Wrkmem(trialinfo,[],[2],[1],[],[],[],[],[],[],[]); +%--Case 12 - 2-Back Non-Targets and Lures +[seltim_2Bnontargandlure,labtim_2Bnontargandlure]=trialsplit_Wrkmem(trialinfo,[],[2],[2 3],[],[],[],[],[],[],[]); +%--Case 13 - 2-Back faces +[seltim_2Bface,labtim_2Bface]=trialsplit_Wrkmem(trialinfo,[1],[2],[],[],[],[],[],[],[],[]); +%--Case 14 - 0-Back faces +[seltim_0Bface,labtim_0Bface]=trialsplit_Wrkmem(trialinfo,[1],[1],[],[],[],[],[],[],[],[]); +%============================================================================== + +% LOCKED ON RESPONSE + +iTRESP=find(strcmp(lockNames,'TRESP')); +trialinfo=lockTrl{iTRESP}; +%===================================================================================== +% INDIVIDUAL CONDITIONS - extracting condition trial indices and mnemonic labels - +%--Case - fixation +[seltresp_fix,labtresp_fix] = trialsplit_Wrkmem(trialinfo, 0,[],[],[],[],[],[],[],[],[]); +%--Case - all images , memory, target types (no settings , default) +[seltresp_allstim,labtresp_allstim] = trialsplit_Wrkmem(trialinfo,[],[],[],[],[],[],[],[],[],[]); +%--Case - OBack +[seltresp_0B,labtresp_0B] = trialsplit_Wrkmem(trialinfo,[], 1,[],[],[],[],[],[],[],[]); +%--Case - 2Back +[seltresp_2B,labtresp_2B] = trialsplit_Wrkmem(trialinfo,[], 2,[],[],[],[],[],[],[],[]); +%--Case - faces +[seltresp_face,labtresp_face] = trialsplit_Wrkmem(trialinfo, 1,[],[],[],[],[],[],[],[],[]); +%--Case - tools +[seltresp_tool,labtresp_tool] = trialsplit_Wrkmem(trialinfo, 2,[],[],[],[],[],[],[],[],[]); +%--Case - targets +[seltresp_targ,labtresp_targ] = trialsplit_Wrkmem(trialinfo,[],[], 1,[],[],[],[],[],[],[]); +%--Case - non targets +[seltresp_nontarg,labtresp_nontarg] = trialsplit_Wrkmem(trialinfo,[],[],[2],[],[],[],[],[],[],[]); +%--Case - lure +[seltresp_lure,labtresp_lure] = trialsplit_Wrkmem(trialinfo,[],[],[3],[],[],[],[],[],[],[]); +%--Case - non targets and lures +[seltresp_nontargandlure,labtresp_nontargandlure] = trialsplit_Wrkmem(trialinfo,[],[],[2 3],[],[],[],[],[],[],[]); +%--Case - 0-Back Targets +[seltresp_0Btarg,labtresp_0Btarg]=trialsplit_Wrkmem(trialinfo,[],1,1,[],[],[],[],[],[],[]); +%--Case - 0-Back Non-Targets and Lures +[seltresp_0Bnontargandlure,labtresp_0Bnontargandlure]=trialsplit_Wrkmem(trialinfo,[],[1],[2 3],[],[],[],[],[],[],[]); +%--Case 11 - 2-Back Targets +[seltresp_2Btarg,labtresp_2Btarg]=trialsplit_Wrkmem(trialinfo,[],[2],[1],[],[],[],[],[],[],[]); +%--Case 12 - 2-Back Non-Targets and Lures +[seltresp_2Bnontargandlure,labtresp_2Bnontargandlure]=trialsplit_Wrkmem(trialinfo,[],[2],[2 3],[],[],[],[],[],[],[]); +%--Case 13 - 2-Back faces +[seltresp_2Bface,labtresp_2Bface]=trialsplit_Wrkmem(trialinfo,[1],[2],[],[],[],[],[],[],[],[]); +%--Case 14 - 0-Back faces +[seltresp_0Bface,labtresp_0Bface]=trialsplit_Wrkmem(trialinfo,[1],[1],[],[],[],[],[],[],[],[]); +%============================================================================== + + + + +%============================================================================= +%============================================================================= +%------- CREATING CONTRASTS =========================================== +% -- Here all the cntrsts to be investigated for this specific paradigm +% -- are hard coded . Any new cntrsts to be added, should added in the +% -- sequence below after the individual condition trial and label selection +% -- has been peformed above +%-------------------------------------------------- +%----------------------------- + +cntrst = []; +%============================================================ +%============================================================ +%{ + %Main structure of a cntrst + + contr =[]; + contr.pipeline = []; + contr.lockmode = []; + contr.mnemtrl = []; + contr.freqband = []; + contr.freq = []; + contr.operation = []; + contr.timeperiods = []; + contr.timedef = []; + contr.baseline = []; + contr.baselinetype= []; + contr.connemetric = []; + contr.invfiltertype =[]; + contr.selection = []; + contr.description = []; + contr.mnemprint = []; +%} +%============================================================ +LockModes={'TIM' % Lock modes represent different pools of trial data. + 'TRESP'}; +Nlm=length(LockModes); + +%--- LockMode 'TIM' +tfavgTimes{1}=[-1:0.025:2]'; +srcTimesFine{1}=[-1:0.025:2]'; +srcTimesCoarseSing{1}=[0 0.5 + 0.5 1 + 1 1.5 + 1.5 2]; +srcTimesCoarseComp{1}=[-0.5 0 + 0 0.5 + 0.5 1 + 1 1.5 + 1.5 2]; +basePeriod{1}=[-0.5 0]; + +%--- LockMode 'TRESP' +tfavgTimes{2}=[-1.25:0.025:1.75]'; +srcTimesFine{2}=[-1.25:0.025:1.75]'; +srcTimesCoarseSing{2}=[-0.25 0.25 + 0.25 0.75 + 0.75 1.25 + 1.25 1.75]; +srcTimesCoarseComp{2}=[-0.75 -0.25 + -0.25 0.25 + 0.25 0.75 + 0.75 1.25 + 1.25 1.75]; +basePeriod{2}=[-0.75 -0.25]; +%======================================================================= +%======================================================================= +%======================================================================= +%======================================================================= +tfavgFreqs=[1:100]; +freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; +%tmpSingCases={'allstim','0B','2B','face','tool','targ','nontarg','lure'}; +%tmpCompCases={ '0B' '2B' +% 'face' 'tool' +% 'targ' 'nontarg' +% 'targ' 'lure' +% 'targ' 'nontarg'}; +tmpSingCases={'0B','2B','face','tool'}; +tmpCompCases={ '0B' '2B' + 'face' 'tool'}; + + +for iLM=1:Nlm, + + lmstr=lower(LockModes{iLM}); + + for iCase=1:length(tmpSingCases) + %-- All Stimuli in Trials cut in n + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freq' , tfavgFreqs,... + 'baseline' , {basePeriod{iLM}},... % In tfavg the Baseline is used ONLY FOR PLOTTING + 'baselinetype' , 'diff',... % In tfavg the Baseline is used ONLY FOR PLOTTING + 'timeperiods' , {tfavgTimes{iLM}},... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesFine{iLM}},... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.baselinetype= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'lockmode' , {LockModes{iLM}},... + 'mnemtrl' , tmpSingCases(iCase),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseSing{iLM}},... + 'timedef' , 'concat',... + 'baseline' , {basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpSingCases{iCase}])},... + 'description' , {eval(['lab',lmstr,'_',tmpSingCases{iCase}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + end + end + + %============================================================ + %============================================================ + %========CONSTRASTS FOR COMPARISONS BETWEEN CONDITIONS ====== + %============================================================ + + for iGroup=1:size(tmpCompCases,1) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'eravg',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'baseline' , {basePeriod{iLM} basePeriod{iLM}},... + 'baselinetype' , 'diff',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tfavg',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freq' , tfavgFreqs,... + 'timeperiods' , {tfavgTimes{iLM} tfavgTimes{iLM}},... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavglcmv',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'timeperiods' , { srcTimesFine{iLM} srcTimesFine{iLM}},... + 'invfiltertype', 'com',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + + for iBand=1:length(freqBands) + + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'srcavgdics',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesFine{iLM}, srcTimesFine{iLM}},... + 'invfiltertype', 'com',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.operation= 'relch'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=newcntrst(... + 'pipeline' , 'tmegconne',... + 'connemetric' , 'coh',... + 'operation' , 'diff',... + 'lockmode' , {LockModes{iLM} LockModes{iLM}},... + 'mnemtrl' , tmpCompCases(iGroup,:),... + 'freqband' , freqBands{iBand},... + 'timeperiods' , {srcTimesCoarseComp{iLM}, srcTimesCoarseComp{iLM}},... + 'timedef' , 'concat',... + 'invfiltertype', 'com',... + 'selection' , {eval(['sel',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['sel',lmstr,'_',tmpCompCases{iGroup,2}])},... + 'description' , {eval(['lab',lmstr,'_',tmpCompCases{iGroup,1}]) eval(['lab',lmstr,'_',tmpCompCases{iGroup,2}])}... + ); + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'imcoh'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'plv'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'psi'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'powc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + cntrst{end+1}=cntrst{end}; + cntrst{end}.connemetric= 'orthopowc'; + cntrst{end}.mnemprint=createcontrmnem(cntrst{end}); + %------------------------------------------------------------- + + %==================================================================== + end + + end + + +end +%============================================================ +%============================================================ +%============================================================ +%============================================================================ + +cntrstList=cntrst; + + +end + + + + + +%************************************************************************** +% ******** END OF MAIN FUNCTION +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** +%************************************************************************** + +function[selection,description]=trialsplit_Wrkmem(... + trialinfo,... + imageTypes,... + memoryTypes,... + targetTypes,... + hasResponded,... + responseTypes,... + prevBlockImageTypes,... + prevBlockMemoryTypes,... + prevTrialTargetTypes,... + prevTrialHasResponded,... + prevTrialResponseTypes) + + +%{ + + +% INPUT PARAMETERS +imageTypes=[1 2]; %param A % 1. 'face', 2. 'place', 3.'body', 4.'tools' 0.fixation +memoryTypes=[1 2]; %param B % 1.'0Back' 2.'2Back' +targetTypes=[1 2 3]; %param C % 1: target', 2: nontarget', 3: lure ???' +hasResponded=[0 1]; %param D % 0. No 1. Yes +responseTypes=[0 1 2]; %param E % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) +prevBlockImageTypes=[1 2 0]; %param F % 1. 'face', 2. 'place', 3.'body', 4.'tools' (As there is always fixation block prior to image block this referes to the previous image block) +prevBlockMemoryTypes=[1 2]; %param G % 1.'0Back' 2.'2Back' +prevTrialTargetTypes=[1 2 3]; %param H % 1: target', 2: nontarget', 3: lure ???' +prevTrialHasResponded=[0 1]; %param I % 0. No 1. Yes +prevTrialResponseTypes=[0 1 2]; %param J % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + %} + + + + %============================================================== + %======== BELOW ARE THE COLUMNS DESCRIPTION for trialinfo matrix produced + % by the trial function. + %trialInfo matrix contains for each presented image the following + %======== info (Each row corresponds to an image): + %===== COLUMNS: + % 1. Run Number + % 2. Block Number + % 3. Image Index in the list of all images. This can be found in variable + % 'encodedRunImageInfo.allImagesList' in the file + % 'WM_runStimInfoDataFile.mat'.The actual image can be directly + % displayed in matlab from the field 'encodedRunImageInfo.allImagesList.allImageData' + % + % 4. imgType :' + % ' 1: Face' + % ' 2: Tools' + % + % 5. memoryType :' + % ' 1: 0-Back' + % ' 2: 2-Back' + % + % 6. targetType 1: target' + % ' 2: nontarget' + % ' 3: lure ???' + % + % 7. Trial start Sample + % 8. Trial end Sample + % 9. Parallel Port Code used for this image : In the stimuli presentation code this was computed as + % curPPCode=20+25*(imgType-1)+5*(blockType-1); see descr. above for + % imgType and blockType + % + % 10. isPressed : + % 0: user did not press any response button + % 1: user pressed a response button + % + % 11. isPressedLate: + % 1: If subject responded after the 2 seconds that the + % image is at the longest displayed and before the next + % trial + % 0: If pressed within the presentation time of the image + % NaN: Otherwise + % + % 12. isDoubleResponse + % 1: user pressed two response buttons in the same trial + % 0: user DID NOT press two response buttons in the same trial + % + % 13. pressedCode: Code of the pressed button (If not pressed NaN) + % + % 14. isCorrect: + % 1: If subject has responded that saw a target when a actual + % target was on or that saw a nontarget when a actual + % nontarget was on + % 0: The opposite of the above. + % NaN: When subject has not responded or has pressed two + % buttons + % + % 15.isLureAsCorrect: + % 1: If subject has responded that saw a target when a lure image of actual + % target was on + % 0: In all other cases that a subject has responded + % NaN: When subject has not responded or has pressed two + % buttons + % + % 16. respTime: The number of samples from onset of Image to response + % + % 17. respDuration: Duration of button press in seconds + %======================================================================== + %++++++++++++++++++++++++++ + % 18. isFirstInBlock + % 19. isLastInBlockk + % ----------- + % 20. prev. Trial: Run Number + % 21. prev. Trial: Block Number + % 22. prev. Trial: Image Index in the list of all images. This can be found in variable + % 'encodedRunImageInfo.allImagesList' in the file + % 'WM_runStimInfoDataFile.mat'.The actual image can be directly + % displayed in matlab from the field 'encodedRunImageInfo.allImagesList.allImageData' + % + % 23. prev. Trial: imgType :' + % ' 1: Face' + % ' 2: Tools' + % + % 24. prev. Trial: memoryType :' + % ' 1: 0-Back' + % ' 2: 2-Back' + % + % 25. prev. Trial: targetType 1: target' + % ' 2: nontarget' + % ' 3: lure ???' + % + % 26. prev. Trial: Trial start Sample + % 27. prev. Trial: Trial end Sample + % 28. prev. Trial: Parallel Port Code used for this image : In the stimuli presentation code this was computed as + % curPPCode=20+25*(imgType-1)+5*(blockType-1); see descr. above for + % imgType and blockType + % + % 29. prev. Trial: isPressed : + % 0: user did not press any response button + % 1: user pressed a response button + % + % 30. prev. Trial: isPressedLate: + % 1: If subject responded after the 2 seconds that the + % image is at the longest displayed and before the next + % trial + % 0: If pressed within the presentation time of the image + % NaN: Otherwise + % + % 31. prev. Trial: isDoubleResponse + % 1: user pressed two response buttons in the same trial + % 0: user DID NOT press two response buttons in the same trial + % + % 32. prev. Trial: pressedCode: Code of the pressed button (If not pressed NaN) + % + % 33. prev. Trial: isCorrect: + % 1: If subject has responded that saw a target when a actual + % target was on or that saw a nontarget when a actual + % nontarget was on + % 0: The opposite of the above. + % NaN: When subject has not responded or has pressed two + % buttons + % + % 34. prev. Trial: isLureAsCorrect: + % 1: If subject has responded that saw a target when a lure image of actual + % target was on + % 0: In all other cases that a subject has responded + % NaN: When subject has not responded or has pressed two + % buttons + % + % 35. prev. Trial: respTime: The number of samples from onset of Image to response + % + % 36. prev. Trial: respDuration: Duration of button press in seconds + % 37. prev. Trial: isFirstInBlock + % 38. prev. Trial: isLastInBlockk + % 39. Is button pressed during onset of the stimulus (New field - not in glasgow scans) + %========================================================================== + + + + condcfg=[]; + condcfg.imageTypes=[1 2]; %param A % 1. 'face', 2.'tools' 0.fixation + condcfg.memoryTypes=[1 2]; %param B % 1.'0Back' 2.'2Back' + condcfg.targetTypes=[1 2 3]; %param C % 1: target', 2: nontarget', 3: lure ???' + condcfg.hasResponded=[0 1]; %param D % 0. No 1. Yes + condcfg.responseTypes=[0 1 2]; %param E % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + condcfg.prevBlockImageTypes=[1 2 0]; %param F % 1. 'face', 2. 'place', 3.'body', 4.'tools' (As there is always fixation block prior to image block this referes to the previous image block) + condcfg.prevBlockMemoryTypes=[1 2 0]; %param G % 1.'0Back' 2.'2Back' + condcfg.prevTrialTargetTypes=[1 2 3]; %param H % 1: target', 2: nontarget', 3: lure ???' + condcfg.prevTrialHasResponded=[0 1]; %param I % 0. No 1. Yes + condcfg.prevTrialResponseTypes=[0 1 2]; %param J % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + + descript=[]; + descript.imageTypes={'fixation','face', 'tool'}; %param A % 1. 'face', 2.'tools' 0.fixation + descript.memoryTypes={'0Back' '2Back'}; %param B % 1.'0Back' 2.'2Back' + descript.targetTypes={'target', 'non-target', 'lure'}; %param C % 1: target', 2: nontarget', 3: lure ???' + descript.hasResponded={'responded' 'noTRESPonded'}; %param D % 0. No 1. Ys + descript.responseTypes={'wrong' 'correct' 'other'}; %param E % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + descript.prevBlockImageTypes={'face', 'tool','fix'}; %param F % 1. 'face', 2. 'place', 3.'body', 4.'tools' + descript.prevBlockMemoryTypes={'0Back' '2Back','fix'}; %param G % 1.'0Back' 2.'2Back' + descript.prevTrialTargetTypes={'targ', 'nontarg', 'lure'}; %param H % 1: target', 2: nontarget', 3: lure ???' + descript.prevTrialHasResponded={'responded' 'noTRESPonded'}; %param I % 0. No 1. Yes + descript.prevTrialResponseTypes={'wrong' 'correct' 'other'}; %param J % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + + + if ismember(condcfg.imageTypes,5)&(numel(condcfg.imageTypes)>1) + error('fixation trials can only be separately extracted from image types - check the image types '); + end + + + if ~isempty(imageTypes), condcfg.imageTypes=imageTypes; end + if ~isempty(memoryTypes), condcfg.memoryTypes=memoryTypes; end + if ~isempty(targetTypes), condcfg.targetTypes=targetTypes; end + if ~isempty(hasResponded), condcfg.hasResponded=hasResponded; end + if ~isempty(responseTypes), condcfg.responseTypes=responseTypes; end + if ~isempty(prevBlockImageTypes), condcfg.prevBlockImageTypes=prevBlockImageTypes; end + if ~isempty(prevBlockMemoryTypes), condcfg.prevBlockMemoryTypes=prevBlockMemoryTypes; end + if ~isempty(prevTrialTargetTypes), condcfg.prevTrialTargetTypes=prevTrialTargetTypes; end + if ~isempty(prevTrialHasResponded), condcfg.prevTrialHasResponded=prevTrialHasResponded; end + if ~isempty(prevTrialResponseTypes), condcfg.prevTrialResponseType=prevTrialResponseTypes; end + + isin_imageTypes=ismember(trialinfo(:,4),condcfg.imageTypes); + isin_memoryTypes=ismember(trialinfo(:,5),condcfg.memoryTypes); + isin_targetTypes=ismember(trialinfo(:,6),condcfg.targetTypes); + isin_hasResponded=ismember(trialinfo(:,10),condcfg.hasResponded); + isin_responseTypes=ismember(trialinfo(:,14),condcfg.responseTypes); + if sum(ismember(condcfg.responseTypes,2))>=1 + isin_responseTypes=(isin_responseTypes|isnan(trialinfo(:,14))); + end + + + isin_FirstBlock=(trialinfo(:,2)==1); + if isequal(condcfg.prevBlockImageTypes,[1 2 0])&isequal(condcfg.prevBlockMemoryTypes,[1 2 0]) + includeFirstBlock=1; + else + includeFirstBlock=0; + end + %--- + isin_prevBlockImageTypes=ismember(trialinfo(:,23),condcfg.prevBlockImageTypes); + if includeFirstBlock, + isin_prevBlockImageTypes=(isin_prevBlockImageTypes|isin_FirstBlock); + end + %--- + isin_prevBlockMemoryTypes=ismember(trialinfo(:,24),condcfg.prevBlockMemoryTypes); + if includeFirstBlock, + isin_prevBlockMemoryTypes=(isin_prevBlockMemoryTypes|isin_FirstBlock); + end + isin_prevTrialTargetTypes=ismember(trialinfo(:,25),condcfg.prevTrialTargetTypes); + if isequal(condcfg.prevTrialTargetTypes,[1 2 3]) + isin_prevTrialTargetTypes(1)=1; + end + isin_prevTrialHasResponded=ismember(trialinfo(:,29),condcfg.prevTrialHasResponded); + if isequal(condcfg.prevTrialHasResponded,[0 1]) + isin_prevTrialHasResponded(1)=1; + end + isin_prevTrialResponseTypes=ismember(trialinfo(:,33),condcfg.prevTrialResponseTypes); + if isequal(condcfg.prevTrialResponseTypes,[0 1]) + isin_prevTrialResponseTypes(1)=1; + end + if sum(ismember(condcfg.prevTrialResponseTypes,2))>=0 + isin_prevTrialResponseTypes=(isin_prevTrialResponseTypes|isnan(trialinfo(:,33))); + end + + + if sum(ismember(condcfg.imageTypes,0))>0,% Fixation case + isInCase=(isin_imageTypes&isin_prevBlockImageTypes&isin_prevBlockMemoryTypes); + isInCaseCurrent=isin_imageTypes; + else % other cases + + isInCase=(isin_imageTypes&isin_memoryTypes&isin_targetTypes&isin_hasResponded&isin_responseTypes&isin_prevBlockImageTypes&isin_prevBlockMemoryTypes&isin_prevTrialTargetTypes&isin_prevTrialHasResponded&isin_prevTrialResponseTypes); + isInCaseCurrent=isin_imageTypes&isin_memoryTypes&isin_targetTypes&isin_hasResponded&isin_responseTypes; + end + + selection=find(isInCase); + + if isequal(condcfg.prevBlockImageTypes,[1 2 0])&isequal(condcfg.prevBlockMemoryTypes,[1 2 0])&isequal(condcfg.prevTrialTargetTypes,[1 2 3])&isequal(condcfg.prevTrialHasResponded,[0 1])&isequal(condcfg.prevTrialResponseTypes,[0 1 2]) + includeFirstOfAll=1; + else + includeFirstOfAll=0; + end + + if includeFirstOfAll + if (sum(selection==1)==0)&(isInCaseCurrent(1)==1), + selection=[1; selection]; + end + end + + + + %{ +condcfg=[]; +condcfg.imageTypes=[1 2 ]; %param A % 1. 'face', 2.'tools' +condcfg.memoryTypes=[1 2]; %param B % 1.'0Back' 2.'2Back' +condcfg.targetTypes=[1 2 3]; %param C % 1: target', 2: nontarget', 3: lure ???' +condcfg.hasResponded=[0 1]; %param D % 0. No 1. Yes +condcfg.responseTypes=[0 1 2]; %param E % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) +condcfg.prevBlockImageTypes=[1 2 3 4 ]; %param F % 1. 'face', 2. 'place', 3.'body', 4.'tools' +condcfg.prevBlockMemoryTypes=[1 2]; %param G % 1.'0Back' 2.'2Back' +condcfg.prevTrialTargetTypes=[1 2 3]; %param H % 1: target', 2: nontarget', 3: lure ???' +condcfg.prevTrialHasResponded=[0 1]; %param I % 0. No 1. Yes +condcfg.prevTrialResponseTypes=[0 1 2]; %param J % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + %} + + + + description=[]; + if ~isequal(condcfg.imageTypes,[1 2]) %param A % 1. 'face', 2.'tools' + description=['imageTypes: ']; + for iTmp=1:length(condcfg.imageTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,descript.imageTypes{condcfg.imageTypes(iTmp)+1}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.memoryTypes,[1 2]) %param B % 1.'0Back' 2.'2Back' + description=[description, 'memoryTypes: ']; + for iTmp=1:length(condcfg.memoryTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.memoryTypes{condcfg.memoryTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.targetTypes,[1 2 3]) %param C % 1: target', 2: nontarget', 3: lure ???' + description=[description, 'targetTypes: ']; + for iTmp=1:length(condcfg.targetTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.targetTypes{condcfg.targetTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.hasResponded,[0 1]) %param D % 0. No 1. Yes + description=[description, 'hasResponded: ']; + for iTmp=1:length(condcfg.hasResponded) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.hasResponded{condcfg.hasResponded(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.responseTypes,[0 1 2]) %param E % 0.Mistaken 1. Correct 2.Nan (Non responded or pressed both) + description=[description, 'responseTypes: ']; + for iTmp=1:length(condcfg.responseTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.responseTypes{condcfg.responseTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.prevBlockImageTypes,[1 2 0]) %param F % 1. 'face', 2.'tools' + description=['prev. Stim. Block - imageTypes: ']; + for iTmp=1:length(condcfg.prevBlockImageTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.prevBlockImageTypes{condcfg.prevBlockImageTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.prevBlockMemoryTypes,[1 2 0]) %param G % 1.'0Back' 2.'2Back' + description=['prev. Stim. Block - memoryTypes: ']; + for iTmp=1:length(condcfg.prevBlockMemoryTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.prevBlockMemoryTypes{condcfg.prevBlockMemoryTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.prevTrialTargetTypes,[1 2 3]) %param H % 1: target', 2: nontarget', 3: lure ???' + description=['prev. Trial - targetTypes: ']; + for iTmp=1:length(condcfg.prevTrialTargetTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.prevTrialTargetTypes{condcfg.prevTrialTargetTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.prevTrialHasResponded,[0 1]) %param I % 0. No 1. Yes + description=['prev. Trial - hasResponded: ']; + for iTmp=1:length(condcfg.prevTrialHasResponded) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.prevTrialHasResponded{condcfg.prevTrialHasResponded(iTmp)}]; + end + description=[description,'\n']; + end + + if ~isequal(condcfg.prevTrialResponseTypes,[0 1 2]) + description=['prev. Trial - responseTypes: ']; + for iTmp=1:length(condcfg.prevTrialResponseTypes) + if iTmp==1, tmpSymbol=''; else tmpSymbol='-and-'; end; + description=[description,tmpSymbol,descript.prevTrialResponseTypes{condcfg.prevTrialResponseTypes(iTmp)}]; + end + description=[description,'\n']; + end + + if isempty(description), + description='allstimuli'; + end + + + + % === The following are the main general division of analysis + %freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; + %pipelines={'eravg','tfavg','srcavglcmv','srcavgdics','conne'}; + %conneCases={'coh','plv','imcoh','psi','powc','orthopowc','xpowc','xpowphase','bigranger'}; + + %== The following are the main subsets of data to apply analysis + %datagroups={'TIM','TRESP'}; + %tmpStimCases={'allstim','0B','2B','face','tool','targ','nontarg','lure'}; + +end + + function [contr]=newcntrst(varargin) + % This subfunction creates a default cntrst function + + contr =[]; + contr.pipeline = ft_getopt(varargin,'pipeline',[]); + %pipelines={'eravg','tfavg','srcavglcmv','srcavgdics','conne'}; + %---------------------------------------------- + contr.lockmode = ft_getopt(varargin,'lockmode',{[]}); + %for WM datagroups={'TIM','TRESP'}; + %---------------------------------------------- + contr.mnemtrl = ft_getopt(varargin,'mnemtrl',{[]}); + % Here go mnemonics for each trial selection i.e. {'0B'} + %---------------------------------------------- + contr.freqband = ft_getopt(varargin,'freqband',[]); + % freqBands={'D','TH','A','Blow','Bhigh','Glow','Gmid','Ghigh'}; + %---------------------------------------------- + contr.freq = ft_getopt(varargin,'freq',[]); + % frequencies to be analysed . Currently only used for tfavg pipeline; + %---------------------------------------------- + contr.operation = ft_getopt(varargin,'operation',[]); + % 'diff','rel', or 'relch' This is used when 2 conditions are compared + %---------------------------------------------- + contr.timeperiods = ft_getopt(varargin,'timeperiods',{[]}); + % This defined the times to be used. If 'all' then the result is + % computed for each time point. For each condition the rows indicate + % The time points for which the analysis is + % performed. If a second column is also + % present then the first column is the + % lower and the second the upper time + % limits within which the analysis should + % be performed. i.e. {[-1 -0.5 + % -0.5 0 + % 0 0.5]} + % Means that the analysis should be + % performed for these 3 time windows + % How data is integrated in each window is + % defined by the .timedef field of the + % cntrst + %---------------------------------------------- + contr.timedef = ft_getopt(varargin,'timedef',[]); + % This how data with a time window should be integrated + % It can be 'avg' or 'concat'. If 'avg' then + % the data within a window is averaged + % before computing the result. If 'concat' + % then all the points within the window are + % used for the computation + %---------------------------------------------- + contr.baseline = ft_getopt(varargin,'baseline',{[]}); + % This defines the time period to be used as baseline + %---------------------------------------------- + contr.baselinetype= ft_getopt(varargin,'baselinetype',[]); + % It can be 'diff','rel' or 'relch' and defines how + % the baseline will be used on the rest of the data + % When 2 conditions are compared this + % defines how baseline has been used in + % each condition. In the case of 2 + % conditions in source space this defines + % how baseline has been used in sensor + % space to derive the inverse solution. + %---------------------------------------------- + contr.connemetric = ft_getopt(varargin,'connemetric',[]); + % conneCases={'coh','plv','imcoh','psi','powc','orthopowc'}; % Next to be implemented ,'xpowc','xpowphase','bigranger' + %---------------------------------------------- + contr.invfiltertype = ft_getopt(varargin,'invfiltertype',[]); + % It can be 'com' or 'ind'. This is used when 2 conditions are compared in source space. If 'com' then a common filter + % is derived from both conditions. .If 'ind' the a filter is derived for each condition + %---------------------------------------------- + contr.selection = ft_getopt(varargin,'selection',[]); + % This is a cell with the indices of the trials to be used + %---------------------------------------------- + contr.description = ft_getopt(varargin,'description',[]); + % This is a cell with basic description of the cntrst (needs to be updated) + %---------------------------------------------- + contr.mnemprint = ft_getopt(varargin,'mnemprint',[]); + % This is the mnemonic that will be used to distinguish the cntrst. Used for saving + %================================================================================================ + end + + + +function[printMnem]= createcontrmnem(incontr) + +pflags=[]; +%pflags.pipeline = 'PI-'; +pflags.lockmode = 'LM-'; +pflags.freqband = 'FB-'; +pflags.operation = 'OP-'; +pflags.timedef = 'TD-'; +pflags.baselinetype = 'BT-'; +pflags.connemetric = 'CM-'; +pflags.invfiltertype ='IT-'; + + +printMnem=[]; +if isempty(incontr.pipeline) + error('Pipeline must be defined in a constrast') + return; +end + +printMnem=[printMnem,incontr.pipeline]; + +Nlms=length(incontr.lockmode); + +if Nlms==1 + printMnem=[printMnem,'_[',pflags.lockmode,incontr.lockmode{1}]; + printMnem=[printMnem,'-',incontr.mnemtrl{1},']']; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + if ~isempty(incontr.freqband) + printMnem=[printMnem,'_[',pflags.freqband,incontr.freqband,']']; + end + if ~isempty(incontr.operation) + printMnem=[printMnem,'_[',pflags.operation,incontr.operation,']']; + end + if ~isempty(incontr.timedef) + printMnem=[printMnem,'_[',pflags.timedef,incontr.timedef,']']; + end + if ~isempty(incontr.baselinetype) + if ~strcmp(incontr.pipeline,'tfavg') + printMnem=[printMnem,'_[',pflags.baselinetype,incontr.baselinetype,']']; + end + end + if ~isempty(incontr.connemetric) + printMnem=[printMnem,'_[',pflags.connemetric,incontr.connemetric,']']; + end + if ~isempty(incontr.invfiltertype) + printMnem=[printMnem,'_[',pflags.invfiltertype,incontr.invfiltertype,']']; + end +elseif Nlms==2 + printMnem=[printMnem,'_[',pflags.lockmode,incontr.lockmode{1}]; + printMnem=[printMnem,'-',incontr.mnemtrl{1}]; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + printMnem=[printMnem,'-versus']; + %printMnem=[printMnem,pflags.lockmode,incontr.lockmode{2}]; This was removed as + %only contrasts from one + %datagroup are + %supported + printMnem=[printMnem,'-',incontr.mnemtrl{2},']']; %Here a hyphen is used instead of underscore. The next underscore shoudl be after the trial mnemonic + if ~isempty(incontr.freqband) + printMnem=[printMnem,'_[',pflags.freqband,incontr.freqband,']']; + end + if ~isempty(incontr.operation) + printMnem=[printMnem,'_[',pflags.operation,incontr.operation,']']; + end + if ~isempty(incontr.timedef) + printMnem=[printMnem,'_[',pflags.timedef,incontr.timedef,']']; + end + if ~isempty(incontr.baselinetype) + if ~strcmp(incontr.pipeline,'tfavg') + printMnem=[printMnem,'_[',pflags.baselinetype,incontr.baselinetype,']']; + end + end + if ~isempty(incontr.invfiltertype) + printMnem=[printMnem,'_[',pflags.invfiltertype,incontr.invfiltertype,']']; + end + if ~isempty(incontr.connemetric) + printMnem=[printMnem,'_[',pflags.connemetric,incontr.connemetric,']']; + end + +else + error('1 or 2 different data Lock Modes are supported'); + return; +end +%================================================================= + + +end diff --git a/trial_functions/trialfun_Motort.m b/trial_functions/trialfun_Motort.m new file mode 100644 index 0000000..053506c --- /dev/null +++ b/trial_functions/trialfun_Motort.m @@ -0,0 +1,22 @@ +function [trl,trlInfoColDescr, trialSummary, scanStartSamp, scanEndSamp, warninfo] = trialfun_Motort( cfg ) + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +[trl,trlInfoColDescr, trialSummary, scanStartSamp, scanEndSamp, warninfo] = trialfun_Motort_BaseExtractAll( cfg ); + +end diff --git a/trial_functions/trialfun_Motort_BaseExtractAll.m b/trial_functions/trialfun_Motort_BaseExtractAll.m new file mode 100644 index 0000000..61e6b94 --- /dev/null +++ b/trial_functions/trialfun_Motort_BaseExtractAll.m @@ -0,0 +1,767 @@ +function[trl,trlInfoColDescr,trialSummary,scanStartSamp,scanEndSamp,warninfo] = trialfun_Motort_BaseExtractAll( cfg ) + +% This is the Trial Definition Function for Motor Task experiment. +% The trigger is derived from the PhotoDiode transients on the trigger channle +%__________________________________________________________________________ +%% Input: +% cfg: Structure with fields that contain parameters for extracting trials +% cfg.datafile: Char string represents filename of raw input MEG data +% cfg.trialdef.stimulCode: Respresting the desired stimulus type to be extracted from the data with following integer values: +% 1 -> Left Hand +% 2 -> Left Foot +% 3 -> Tongue +% 4 -> Right Hand +% 5 -> Right Foot +% 6 -> Fixation +% cfg.trialdef.TrigBasedOnEMG = 'yes' or 'no' (default = 'yes') +% cfg.trialdef.cutMode = 'trials' or 'blocks' (default = 'trials') +% cfg.trialdef.prestimTime: Desired time interval (in seconds) before Trigger onset. +% Must be positive. In 'blocks' mode, prestimTime w.r.t. first event. +% cfg.trialdef.poststimTime: Desired time interval (in seconds) after Trigger onset. +% Must be positive. In 'blocks' mode, poststimTime w.r.t. last event. +% cfg.trialdef.plotresults = 'yes' or 'no' (default = 'no') +% cfg.trialdef.summaryfile: name of text file in order to save a sumamry of +% the trial information as this is read from the triggers. It is used as a +% quality check. If is empty nothing is printed. +%__________________________________________________________________________ +%% Output: +% trl: Ntrials-by-3 matrix with the trial definition +% Column 1: Start sample for trials +% Column 2: End sample for trials +% Column 3: Offset of beginning of each trial from Trigger onset in Samples (i.e. -100). +%__________________________________________________________________________ +%% === Example 1: +% cfg = []; +% cfg.datafile = 'xxx\c,rfDC';% xxx is directory of the raw MEG scan +% cfg.trialfun = 'trialfun_Motort_Base'; +% cfg.trialdef.stimulCode = 4; % 1->Left Hand, 2->Left Foot, 3->Tongue, 4->Right Hand, 5->Right Foot, 6->Fixation +% cfg.trialdef.prestimTime = 0.1; +% cfg.trialdef.poststimTime = 0.5; +% %cfg.trialdef.TrigBasedOnEMG = 'yes';%(default = 'yes') +% %cfg.trialdef.cutMode = 'trials';%(default = 'trials') +% %cfg.trialdef.plotresults = 'no';%(default = 'no') +% cfgDefTr = ft_definetrial(cfg); +% cfgDefTr.dataformat = '4d'; +% cfgDefTr.headerformat = '4d'; +% dataRaw = ft_preprocessing(cfgDefTr); +%% === Example 2: +% cfg = []; +% cfg.datafile = 'xxx\c,rfDC';% xxx is directory of the raw MEG scan +% cfg.trialfun = 'trialfun_Motort_Base'; +% cfg.trialdef.stimulCode = 2; % 1->Left Hand, 2->Left Foot, 3->Tongue, 4->Right Hand, 5->Right Foot, 6->Fixation +% cfg.trialdef.prestimTime = 0; +% cfg.trialdef.poststimTime = 0.5; +% cfg.trialdef.TrigBasedOnEMG = 'no';%(default = 'yes') +% cfg.trialdef.cutMode = 'blocks';%(default = 'trials') +% cfg.trialdef.plotresults = 'yes';%(default = 'no') +% cfgDefTr = ft_definetrial(cfg); +% cfgDefTr.dataformat = '4d'; +% cfgDefTr.headerformat = '4d'; +% dataRaw = ft_preprocessing(cfgDefTr); + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +%__________________________________________________________________________ + +%% +if ~isfield(cfg.trialdef, 'TrigBasedOnEMG'), cfg.trialdef.TrigBasedOnEMG = 'yes'; end +if ~isfield(cfg.trialdef, 'cutMode'), cfg.trialdef.cutMode = 'trials'; end +if ~isfield(cfg.trialdef, 'plotresults'), cfg.trialdef.plotresults = 'no'; end + +datafile = cfg.datafile; +%stimulCode = [1 2 4 5]; %These define motor block codes; for Fixation the code is 6 which is hard coded in the code processing fixation + +TrigBasedOnEMG = cfg.trialdef.TrigBasedOnEMG; +cutMode = cfg.trialdef.cutMode;% 'trials' or 'blocks' +prestimTime = cfg.trialdef.prestimTime; +poststimTime = cfg.trialdef.poststimTime; +montage=cfg.trialdef.montage; + +% summaryfile=cfg.trialdef.summaryfile; +if strcmpi(cfg.trialdef.plotresults , 'yes' ), + plot_flag = 1; + %1->Left Hand, 2->Left Foot, 3->Tongue, 4->Right Hand, 5->Right Foot, 6->Fixation + %titletxt = {'Left Hand', 'Left Foot', 'Tongue', 'Right Hand', 'Right Foot', 'Fixation' }; +else + plot_flag = 0; +end + + +%{ +% Trigger information +%----------------------------------------------------------------- +According to E-prime files the Trigger codes are the following: +%---------- +LeftHandCueProcedure : 18 +CrossLeftPROC: 22 +%---------- +LeftFootCuePROC: 34 +CrossLeftPROC : 38 +%---------- +RightHandCuePROC: 66 +CrossRightPROC: 70 +%---------- +RightFoottCuePROC: 130 +CrossRightPROC: 134 +%---------- +These can be modelled as: + +%----------------------- +Photodiode: +The photodiode is activated on the onset of the Cues and the onset of the +flashing Cross. +%} + +%========================================================================== +%% READ TRIGGER FROM DATA FILES +hdr = ft_read_header(datafile); +Fsample = hdr.Fs; +detailedTrigger = ft_read_data(datafile,'chanindx',1,'header',hdr,'eventformat','4d','dataformat','4d'); +Nsamples=length(detailedTrigger); +prestimSamples = floor(prestimTime*Fsample); +poststimSamples = floor(poststimTime*Fsample); + +% SPLIT TRIGGERS FROM PARALLEL PORT AND TRIGGERS FROM PHOTODIODE +trigComb=detailedTrigger; +trigParal=trigComb-256*(floor(trigComb./256)); +trigPhoto=256*(floor(trigComb./256)); + +%---------------------------------------------------------- +% the following contains informatin about e-prime triggers for cues and +% crosses. COL1: trigger calue. COL2: 1:cue 2:cross. COL3:1:Left Hand +% 2:Left Foot 4: Right Hand 5: Right Foot 0:Fixation +% The trigger value of 2 at then end corresponds to fixation block + +epTrigInfo=[18 1 1 + 22 2 1 + 34 1 2 + 38 2 2 + 66 1 4 + 70 2 4 + 130 1 5 + 134 2 5 + 2 0 6]; + +%----------------------------------------------------------- +% Find the onsets of the photodiode and the onsets of cues and crosses ni +% eprime. These 2 sets should have the same number of events and their time +% difference should be in the order of milliseconds jsut due to the +% projector delay. + +epTrigsCueAndCross=epTrigInfo(ismember(epTrigInfo(:,2),[1 2]),1)'; + +%--- +difTrigParal=diff(trigParal); +fwdTrigParal=trigParal(2:end); +bwdTrigParal=trigParal(1:end-1); +%--- +difTrigPhoto=diff(trigPhoto); +fwdTrigPhoto=trigPhoto(2:end); +bwdTrigPhoto=trigPhoto(1:end-1); +%--- +indParalUP=find(ismember(fwdTrigParal,epTrigsCueAndCross) & (fwdTrigParal~=bwdTrigParal) )+1; % Find onset of parallel trigger for cues and crosses +indPhotoUP=find(difTrigPhoto>0)+1; +%% +%========================================= +%-- Check correspondence between photodiode and parallel port triggered +%-- cue and cross events. +% Create stimMap variable with Columns: +% COL1: 1:cue 2:cross 0: fixation. +% COL2: 1:Left Hand, 2:Left Foot 4: Right Hand 5: Right Foot 0:Fixation +% COL3: Event onset based on Parallel port trigger +% COL4: Event onset based on Parallel port trigger +% COL5: Block Number in Run +% COL6: Trial Sequence number within a block of Flashing Cross events. For cues and fixation this is 0. +% COL7: Parallel Port Trigger Code +warninfo=[]; + +badThresh=4/60; %This is the maximum time difference threshold between a trigger in photodiode and the corresponding trigger in parallel port; +badThreshSamps=floor(badThresh.*hdr.Fs); +stimMap=[]; +if length(indParalUP)~=length(indPhotoUP) + %error('The number of onsets from photodiode is not the same with the number of cue and cross onset derived from e-prime trigger values'); + warning('The number of onsets from photodiode is not the same with the number of cue and cross onset derived from e-prime trigger values'); + warninfo.dif_ph2pp.difNevents=length(indPhotoUP)-length(indParalUP); + + countStims=1; + for iStim=1:length(indParalUP) + indIn=find(abs(indPhotoUP-indParalUP(iStim))<=badThreshSamps,1,'first'); + if isempty(indIn) + error(['No close-by photodiode trigger found for stimulus at sample:',num2str(indParalUP(iStim))]); + else + addinfo=epTrigInfo(find(ismember(epTrigInfo(:,1),trigParal(indParalUP(iStim)))),2:end); + stimMap(countStims,1:4)=[addinfo indParalUP(iStim) indPhotoUP(indIn)]; + stimMap(countStims,7)=trigParal(indParalUP(iStim)); + end + countStims=countStims+1; + end +else + tmpTimeDif=(indPhotoUP-indParalUP)./hdr.Fs; + indbad=find(abs(tmpTimeDif)>badThresh); + if ~isempty(indbad), + warning('there seem to be time difference > 4 refresh frames between the parallel port and photodiode triggers for some stimulus') + warninfo.dif_ph2pp.samp_longdelay=indPhotoUP(indbad); + + stimMap=[]; + countStims=1; + for iStim=1:length(indParalUP) + indIn=find(abs(indPhotoUP-indParalUP(iStim))<=badThreshSamps,1,'first'); + if ~isempty(indIn) + error(['No close-by photodiode trigger found for stimulus at sample:',num2str(indParalUP(iStim))]); + else + addinfo=epTrigInfo(find(ismember(epTrigInfo(:,1),trigParal(indParalUP(iStim)))),2:end); + stimMap(countStims,1:4)=[addinfo indParalUP(iStim) indPhotoUP(indIn)]; + stimMap(countStims,7)=trigParal(indParalUP(iStim)); + + end + countStims=countStims+1; + end + else + stimMap=[]; + for iStim=1:length(indParalUP) + addinfo=epTrigInfo(find(ismember(epTrigInfo(:,1),trigParal(indParalUP(iStim)))),2:end); + stimMap(iStim,1:4)=[addinfo indParalUP(iStim) indPhotoUP(iStim)]; + stimMap(iStim,7)=trigParal(indParalUP(iStim)); + end + end +end + +tmpDifPH2PP=stimMap(:,4)-stimMap(:,3); +if nanmean(abs(tmpDifPH2PP)./Fsample) > (2/60) + warning('The avergae difference between Photodiode and Parallel Port Triggers is more than 2 monitor refresh rates.'); + warninfo.dif_ph2pp.avgdelay=nanmean(abs(tmpDifPH2PP)./Fsample); +end +%------------------------------------------------------------------- +%% Add fixation bock information in stimMap variable. +% Fixation blocks can only be defined by the parallel port + +indParalFix=find((fwdTrigParal==2)&(bwdTrigParal~=2))+1; + +Nfix=length(indParalFix); +addinfo=epTrigInfo(find(ismember(epTrigInfo(:,1),2)),2:end); +stimMap=[stimMap; [repmat(addinfo,Nfix,1) indParalFix' indParalFix' nan(Nfix,2,1) repmat(2,Nfix,1)]]; % add fixation block information to stimMap. +stimMap=sortrows(stimMap,3); % sort all events so that fixation blocks end up at the right sequence +%% Derive the sequence of cross onsets within each block of trials and the sequence of Blocks +blockNum=0; +seqTrlNum=1; +for iEv=1:size(stimMap,1) + + if stimMap(iEv,1)==1, % if a cue + blockNum=blockNum+1; + stimMap(iEv,5)=blockNum; + stimMap(iEv,6)=0; % This is a cue so trial sequence number set to 0 + seqTrlNum=1; + elseif stimMap(iEv,1)==2, % cross case + stimMap(iEv,5)=blockNum; + stimMap(iEv,6)=seqTrlNum; + seqTrlNum=seqTrlNum+1; + elseif stimMap(iEv,1)==0, % fixatin case + blockNum=blockNum+1; + stimMap(iEv,5)=blockNum; + stimMap(iEv,6)=0; % This is fixation so trial sequence number set to 0 + seqTrlNum=1; + end +end +%% +%------------------------------- +% Fixation blocks cannot be defined by the photodiode directly as there is +% not a photrodiode trigger for this. But it can be inferred by adding the +% inter stimulus interval of flashing cross , 1200 msec, to the time of +% the last photodiode trigger from flashing cross within a block. + +ISI=1.2; %inter stimulus interval in sec +ISIsamps=ceil(ISI.*hdr.Fs); +tmpIndFix=find(stimMap(:,1)==0); +indPhotoFix=stimMap(tmpIndFix-1,4)+ISIsamps; +stimMap(tmpIndFix,4)=indPhotoFix; + +%================================================================= +%% FORM MOTOR TRIAL INFO based on Flashing Cross +% This creates a trial information matrix only for the flashing cross +% trials with the following columns: +%Columns: +%------------------- +% 1. Block Index +% 2. Block Stim Code: 1:Left Hand, 2:Left Foot 4: Right Hand 5: Right Foot +% 3. Trial Index in Block +% 4. Trial Onset Trigger Sample +% 5. prev. Block Stim Code + +motorTrialInfo=[]; +prevBlockStimcode=nan; +countCrosses=1; +for iEv=1:length(stimMap) + if (stimMap(iEv,1)==1)|(stimMap(iEv,1)==0), % cue or fixatoin + curBlockNum=stimMap(iEv,5); + if iEv>1, prevBlockStimcode=stimMap(iEv-1,2); end; + else + motorTrialInfo(countCrosses,:)=[stimMap(iEv,[5 2 6 4]) prevBlockStimcode]; + countCrosses=countCrosses+1; + end +end + +%=============================================== +%% FROM BLOCK INFO +% This creates a matrix with information about all blocks. +% allBlockInfo +%===Columns=== +% 1. Block Index +% 2. Stim Code +% 3. Cue Onset sample . For fixation this is the start sample of the fixation block +% 4. Cue Offset sample . For fixation nan +% 5. Cross 1 Onset Sample. For fixation nan +% 6. Cross 2 Onset Sample. For fixation nan +% 7. Cross 3 Onset Sample. For fixation nan +% 8. Cross 4 Onset Sample. For fixation nan +% 9. Cross 5 Onset Sample. For fixation nan +% 10. Cross 6 Onset Sample. For fixation nan +% 11. Cross 7 Onset Sample. For fixation nan +% 12. Cross 8 Onset Sample. For fixation nan +% 13. Cross 9 Onset Sample. For fixation nan +% 14. Cross 10 Onset Sample. For fixation nan +% 15. Block End Sample +% 16. Previous Block Code +fixBlockDur=15; % This is the expected duration of fixation blocks in sec +fixBlockSamps=floor(fixBlockDur*hdr.Fs); + +indNonCross=find(stimMap(:,1)~=2); +noncrossMap=stimMap(indNonCross,:); +allBlockInfo=[]; +countBlocks=1; +for iEv=1:length(noncrossMap) + if (noncrossMap(iEv,1)==1), % cue + allBlockInfo(countBlocks,1:3)=stimMap(indNonCross(iEv),[5 2 4]); + % Find the sample of cue off from the photodiode trigger + indCueOff=find(([1:Nsamples-1]>stimMap(indNonCross(iEv),4))&(fwdTrigPhoto~=trigPhoto(stimMap(indNonCross(iEv),4)))&(bwdTrigPhoto==trigPhoto(stimMap(indNonCross(iEv),4))),1,'first')+1; + % % Find the sample of cue off from the parallel port trigger + % indCueOff=find(([1:Nsamples-1]>stimMap(indNonCross(iEv),3))&(fwdTrigParal~=stimMap(indNonCross(iEv),7))&(bwdTrigParal==stimMap(indNonCross(iEv),7)),1,'first'); + allBlockInfo(countBlocks,4)=indCueOff; + allBlockInfo(countBlocks,5:14)=stimMap(indNonCross(iEv)+1:indNonCross(iEv)+10,4)'; + allBlockInfo(countBlocks,15)=stimMap(indNonCross(iEv+1),4)-1;%Set the end of the block to the sample just before the onset of the next block + elseif (noncrossMap(iEv,1)==0), % fixation + allBlockInfo(countBlocks,1:3)=stimMap(indNonCross(iEv),[5 2 4]); + allBlockInfo(countBlocks,4:14)=nan; + if iEv1 + allBlockInfo(countBlocks,16)=noncrossMap(iEv-1,2); % prev Block Stim Type + else + allBlockInfo(countBlocks,16)=nan; % prev Block Stim Type + end + countBlocks=countBlocks+1; +end + +%------------------------------------------- +%====================================================== +%% Identify the start and end of scan as the onset of the first cue and the end of the last block +% A default temporal padding of 3 second is used in either end +defEndPad=floor(3*Fsample); +scanStartSamp=allBlockInfo(1,3)-defEndPad; +if scanStartSamp<1 + scanStartSamp=1; +end +scanEndSamp=allBlockInfo(end,15)+defEndPad; +if scanEndSamp>Nsamples, + scanEndSamp=Nsamples; +end +%====================================================== +%% +%================================================== +%% FORM FIXATION PSEUDO TRIALS +% This code takes the fixation blocks and divides them in pseudo trials of +% the same length as the Inter Stimulus Interval. +%------------------- +% 1. Block Index +% 2. Block Stim Code: 6: Fixation +% 3. pseudo Trial Index in Block +% 4. pseudo Trial Onset Trigger Sample +% 5. prev. Block Stim Code +fixBlockInfo=allBlockInfo((allBlockInfo(:,2)==6),:); + +halfISIsamps=floor(ISIsamps./2); +fixTrialInfo=[]; +for iBlock=1:size(fixBlockInfo,1), + indxStart = fixBlockInfo(iBlock,3)+halfISIsamps; %Exclude the first 600msec from fixation + indxEnd = fixBlockInfo(iBlock,15)-ISIsamps; + pseudoTrlStartSamp=[indxStart:ISIsamps:indxEnd]; + + tmpNtrials=length(pseudoTrlStartSamp); + fixTrialInfo=[fixTrialInfo; + repmat(fixBlockInfo(iBlock,1),tmpNtrials,1),...%1 + repmat(6,tmpNtrials,1),...%2 + [1:tmpNtrials]',...%3 + pseudoTrlStartSamp',...%4 + repmat(fixBlockInfo(iBlock,16),tmpNtrials,1)];%5 +end + +%================================================== +%================================================== +%================================================== +%================================================== +%================================================== +%% LOAD EMG DATA + +emgExpChans={'EMG_LH' + 'EMG_RH' + 'EMG_LF' + 'EMG_RF'}; + +hasEMG=1; +[ind1,ind2]=match_str(emgExpChans,montage.labelnew); +if isempty(ind1) + hasEMG=0; + disp('WARNING ! No EMG channels with the standard naming convention were found'); + disp('Assuming no EMG is available. Trials will be extracted based only on the flashing cross.'); +end + +if hasEMG + if (length(ind1)~=4) + error('Not all EMG channels are present or there are duplicates.'); + return; + end + if length(unique(emgExpChans(ind1)))~=4 + error('There are EMG channel duplicates.'); + return; + end + tmpTra=montage.tra(ind2,:); + [k1,l1]=find(tmpTra~=0); + indOrig=unique(l1); + chanOrig=montage.labelorg(indOrig); + chanNew=emgExpChans(ind1); + newTra=tmpTra(:,indOrig); + + newmontage=montage; + newmontage.labelorg=chanOrig; + newmontage.labelnew=chanNew; + newmontage.tra=newTra; + + [ind1,ind2]=match_str(newmontage.labelorg,hdr.label); + if (length(ind2)~=4) + error('Not all EMG channels were found in the dataset.'); + return; + end + + emgdataorig= ft_read_data(datafile,'chanindx',ind2,'header',hdr,'eventformat','4d','dataformat','4d'); + emgdatanew=newmontage.tra*emgdataorig; + + [ind1,ind2]=match_str(chanNew,'EMG_LH'); + emgdata_LH=emgdatanew(ind1,:); + [ind1,ind2]=match_str(chanNew,'EMG_LF'); + emgdata_LF=emgdatanew(ind1,:); + [ind1,ind2]=match_str(chanNew,'EMG_RH'); + emgdata_RH=emgdatanew(ind1,:); + [ind1,ind2]=match_str(chanNew,'EMG_RF'); + emgdata_RF=emgdatanew(ind1,:); + clear emgdataorig emgdatanew; +else + emgdata_LH=[]; + emgdata_LF=[]; + emgdata_RH=[]; + emgdata_RF=[]; +end + +%================================================================== +%% MOTOR TRIAL INFO BASED ON EMG +%Columns: +%------------------- +% 1. Block Index +% 2. Block Stim Code +% 3. Trial Index in Block (This is derived by finding the flash cross onset just before the EMG onset) +% 4. Trial Onset EMG Sample +% 5. prev. Block Stim Code +% 6. Time from EMG onset to the previous Flashing Cross + +motorBlockColumnDescription={''}; % IF BLOCKS ARE ANALYZED AS A WHOLE ADD DESCRIPTION +motorTrlColumnDescription={' 1. Block Index ' + ' 2. Block Stim Code: 1-Left Hand, 2 - Left Foot, 4 - Right Hand. 5 - Right Foot, 6 - Fixation' + ' 3. Trial Index in Block (This is derived by finding the flash cross onset just before the EMG onset)' + ' 4. Trial Onset EMG Sample' + ' 5. prev. Block Stim Code' + ' 6. Time from EMG onset to the previous Flashing Cross'}; + + +motorBlockInfo=allBlockInfo((allBlockInfo(:,2)~=6),:); + +tmpInfoEMG=[]; +if hasEMG + for iBlock = 1 : size(motorBlockInfo,1) + indBlockStart = motorBlockInfo(iBlock,4); % Cue Off point + indBlockEnd = motorBlockInfo(iBlock,15); % End of Block + tmpBlockStimcode = motorBlockInfo(iBlock,2); + + + % trg1 = bitand( trg(ix1) , trgmsk(tmpBlockCode) ) .* (bitand( trg(ix1) , trgcode(tmpBlockCode) ) == trgcode(tmpBlockCode) ); + indArrayBlock=indBlockStart:indBlockEnd; + trigBlock=trigPhoto(indArrayBlock); + + if tmpBlockStimcode==1, + emgdata=emgdata_LH; + elseif tmpBlockStimcode==2, + emgdata=emgdata_LF; + elseif tmpBlockStimcode==4, + emgdata=emgdata_RH; + elseif tmpBlockStimcode==5, + emgdata=emgdata_RF; + end + if isempty(emgdata) + continue; + end + + trigEMG = Extract_EMG_Trigger( emgdata(indBlockStart:indBlockEnd) , hdr.Fs , trigBlock ); + + indTrlEMG = indArrayBlock( find( diff(trigEMG) > 0 ) + 1 ); + tmpNtrials=length(indTrlEMG); + tmpInfoEMG=[tmpInfoEMG; + repmat(motorBlockInfo(iBlock,1),tmpNtrials,1),...%1 + repmat(tmpBlockStimcode,tmpNtrials,1),...%2 + nan(tmpNtrials,1),...%3 + indTrlEMG',...%4 + repmat(motorBlockInfo(iBlock,16),tmpNtrials,1),...%5 + nan(tmpNtrials,1)];%6 + + + end + + % if isempty(tmpInfoEMG) + % disp( [ 'There is no trial in "' cfg.datafile '"']) + % disp( 'based on parameters of following cfg:') + % disp( cfg.trialdef ) + % trl = []; + % return; + % end + %================================================== + %-- Fuse with trial info from flashing cross + NemgTrials=size(tmpInfoEMG); + + for iTrial=1:NemgTrials, + tmpIndx=find(motorTrialInfo(:,4)Nsamples); + trl(i1,:)=Nsamples; + psfixtrl=[]; + titletxt = ['cutMode:blocks']; + + trlInfoColDescr=motorBlockColumnDescription; + +elseif strcmp(cutMode,'trials') + + titletxt = ['cutMode:trials']; + if strcmpi(TrigBasedOnEMG, 'yes') + if ~isempty(motorTrialInfoEMG) + trl=[motorTrialInfoEMG(:,4)-prestimSamples motorTrialInfoEMG(:,4)+poststimSamples -repmat(prestimSamples,size(motorTrialInfoEMG,1),1) motorTrialInfoEMG]; + psfixtrl=[fixTrialInfo(:,4) fixTrialInfo(:,4)+ISIsamps repmat(0,size(fixTrialInfo,1),1) fixTrialInfo nan(size(fixTrialInfo,1),1)]; + titletxt=[titletxt,' : ','lockedon:EMG']; + else + trl=[]; + psfixtrl=[]; + titletxt=[titletxt,' : ','lockedon:EMG no trials found']; + end + elseif strcmpi(TrigBasedOnEMG, 'no') + trl=[motorTrialInfo(:,4)-prestimSamples motorTrialInfo(:,4)+poststimSamples -repmat(prestimSamples,size(motorTrialInfo,1),1) motorTrialInfo]; + psfixtrl=[fixTrialInfo(:,4) fixTrialInfo(:,4)+ISIsamps repmat(0,size(fixTrialInfo,1),1) fixTrialInfo]; + titletxt=[titletxt,' : ','lockedon:Flash']; + end + if ~isempty(trl) + [i1,j1]=find(trl(:,2)>Nsamples); + trl(i1,:)=[]; + end + if ~isempty(psfixtrl) + [i1,j1]=find(psfixtrl(:,2)>Nsamples); + psfixtrl(i1,:)=[]; + end + + trlInfoColDescr=motorTrlColumnDescription; +end + + + + +%========================================================= +%% -- MERGE TRL with FIXATION PSEUDO TRL (makes a difference only when data is cut in trials) +trl=[trl; psfixtrl]; + + +trialSummary=[]; +%=========================================== +%% --- Compute summary of trial information to print is summary file +trialSummary.Nblocks=size(allBlockInfo,1); +trialSummary.Nblocks_Fixation=length(find(allBlockInfo(:,2)==6)); +trialSummary.Nblocks_LH=length(find(allBlockInfo(:,2)==1)); +trialSummary.Nblocks_LF=length(find(allBlockInfo(:,2)==2)); +trialSummary.Nblocks_RH=length(find(allBlockInfo(:,2)==4)); +trialSummary.Nblocks_RF=length(find(allBlockInfo(:,2)==5)); +trialSummary.Ntrials=size(motorTrialInfo,1); +trialSummary.Ntrials_LH=length(find(motorTrialInfo(:,2)==1)); +trialSummary.Ntrials_LF=length(find(motorTrialInfo(:,2)==2)); +trialSummary.Ntrials_RH=length(find(motorTrialInfo(:,2)==4)); +trialSummary.Ntrials_RF=length(find(motorTrialInfo(:,2)==5)); +if ~isempty(motorTrialInfoEMG) + trialSummary.NtrialsEMG_LH=length(find(motorTrialInfoEMG(:,2)==1)); + trialSummary.NtrialsEMG_LF=length(find(motorTrialInfoEMG(:,2)==2)); + trialSummary.NtrialsEMG_RH=length(find(motorTrialInfoEMG(:,2)==4)); + trialSummary.NtrialsEMG_RF=length(find(motorTrialInfoEMG(:,2)==5)); + trialSummary.RatioTrlEMG_LH=trialSummary.NtrialsEMG_LH./trialSummary.Ntrials_LH; + trialSummary.RatioTrlEMG_LF=trialSummary.NtrialsEMG_LF./trialSummary.Ntrials_LF; + trialSummary.RatioTrlEMG_RH=trialSummary.NtrialsEMG_RH./trialSummary.Ntrials_RH; + trialSummary.RatioTrlEMG_RF=trialSummary.NtrialsEMG_RF./trialSummary.Ntrials_RF; +else + trialSummary.NtrialsEMG_LH=0; + trialSummary.NtrialsEMG_LF=0; + trialSummary.NtrialsEMG_RH=0; + trialSummary.NtrialsEMG_RF=0; + trialSummary.RatioTrlEMG_LH=0; + trialSummary.RatioTrlEMG_LF=0; + trialSummary.RatioTrlEMG_RH=0; + trialSummary.RatioTrlEMG_RF=0; +end +%=========================================== + +end +%% END OF MAIN FUNCTION + +%%========================================================================= +%%========================================================================= +%%========================================================================= +%% +function [ emg_trg, Trg_EMG_delay, emgflt] = Extract_EMG_Trigger( emg , Fs , trg , PLT_FLG1) +EMG_STD_THRESHOLD = 0; % Thereshold for z-transform +if nargin < 4 + PLT_FLG1 = 0;% falg for end results plot +end +PLT_FLG2 = 0; % falg for very detailed plots + +emg = emg(:)'; +mx = max( trg ); +mn = min( trg ); +ay = abs(emg); +trg = mean( ay( ay > 5*mean(ay) ) ) * (trg - mn )/(mx - mn); + +emgflt = ft_preproc_highpassfilter(emg, Fs, 10); % highpassfilter +emghlb = abs(hilbert(emgflt')'); % hilbert transform +emgcnv = conv(emghlb , ones(1,round(Fs/5)), 'same'); % smooth using convolution +emgstd = (emgcnv - mean(emgcnv))/std(emgcnv); % z-transform, i.e. mean=0 and stdev=1 +emgtrl = emgstd > EMG_STD_THRESHOLD; % detect the muscle activity + +ixtrg = find( diff([trg(1);trg(:)]) > 0 ); +ixemgp = find( diff([emgtrl(1);emgtrl(:)]) > 0 ); +ixemgn = find( diff([emgtrl(1);emgtrl(:)]) < 0 ); + +dt = mean( diff(ixtrg) ); +emg_trg = zeros( size(trg) ); +emgstd2 = emgstd; +Trg_EMG_delay = []; +for i = 1 : length(ixtrg) + ixtrg1 = ixtrg(i) + round( -0.38*dt:0.58*dt ); + [mvtrg mitrg1] = max( emgstd2(ixtrg1) ); + if mvtrg > EMG_STD_THRESHOLD + ix1megU = ixemgp(max( find( ixemgp < ( mitrg1 + ixtrg1(1) ) ) ));% Trigger based on EMG starting time + if ~isempty(ix1megU) + ix1megD = ixemgn(min( find( ixemgn > ix1megU ) )); + if ~isempty(ix1megD) + %========= Trigger based on EMG pick time ================================= + % tmp = emgstd(ix1megU:ix1megD); + % tmpix = find( tmp > .75*max(tmp) ); + % tmpix = round( sum(tmpix .* tmp(tmpix)) / sum( tmp(tmpix) ) ); + % ix1megU = ix1megU + tmpix - 1; + %========================================================================== + %========= Trigger based on thresold of median of EMG trial =============== + tmp = abs(emgflt(ix1megU:ix1megD)); + mtmp = mean( tmp( tmp >= median(tmp) ) ); + tmpix = find( tmp >= mtmp ); + ix1megU = ix1megU + min(tmpix) - 1; + %========================================================================== + emg_trg(ix1megU:ix1megD)=1; + emgstd2(ix1megU:ix1megD)=0; + emgstd2(1:ix1megD)=0; + Trg_EMG_delay(i) = ix1megU - ixtrg(i); + else + Trg_EMG_delay(i) = NaN; + end + else + Trg_EMG_delay(i) = NaN; + end + else + Trg_EMG_delay(i) = NaN; + end + % plot results for each trial + if PLT_FLG2 + figure;clf;%if i== 1, clf, end + plot( emgstd , ':' ) + hold on + plot( emgstd2 ) + plot( emgtrl ,'g') + plot( emg_trg ,'r') + plot(2*trg/max(trg),'k') + plot(ixtrg1,2*trg(ixtrg1)/max(trg),'c') + plot( abs(emgflt)/max(abs(emgflt)),'b' ) + grid on + end +end + +% plot results for whole block +if PLT_FLG1 + figure;clf + plot( 1.0*abs(emgflt)/max(emgflt)*max(emgstd), 'g' ); + hold on;grid on + plot( emgstd); + plot( 1.5*trg/max(trg) , 'r' ) + plot( emg_trg , 'k','linewidth' , 2) + legend('abs of EMG' , 'EMG after HighPass, Hilbert, Smooth & Z-Trans' , 'Flash on', 'EMG trigger') + xlim( [ 0 length(trg)] ) + + % figure;clf + % hist(emgstd,1000) + % hold on + % plot( [ mean(emgstd) mean(emgstd)], [0 150] , '-r' , 'linewidth' , 2 ) + % title( 'Histogram of z-transform' ) + + % figure;clf + % subplot(311) + % plot( emg ); hold on; + % plot( .75*trg , 'r') + % legend('EMG' , 'Flash on') + % subplot(312) + % plot( emghlb);hold on; + % plot( .75*trg , 'r') + % legend('Hilbert EMG' , 'Flash on') + % subplot(313) + % plot( emgcnv); hold on; + % plot( .25*max(emgcnv)* trg/max(trg) , 'r') + % legend('Hilbert EMG' , 'Flash on') + +end +end + + diff --git a/trial_functions/trialfun_Restin.m b/trial_functions/trialfun_Restin.m new file mode 100644 index 0000000..65f2c75 --- /dev/null +++ b/trial_functions/trialfun_Restin.m @@ -0,0 +1,69 @@ +function[trl]=trialfun_Restin(inputCfg) + +% This is a generic Trial Definition Function for the experiments in which +% no trigger has been sent to the parallel port. +% In these cases the user defined the duration of each trial and the entire +% scan data is split in trials with the specified duration. +% +% +% Input: +% inputCfg: Structure with fields that contain parameters +% for extracting trials +% Required Fields in inputCfg: +% --------------------------------------------------------- +% .datafile: Char String with the filename of the raw data +% from the MEG scan +% .trialdef.trialDuration: Desired Trial duration - in seconds. +% Must be positive. +% --------------------------------------------------------- +% +% Output: +% trl: Matrix of size Ntrialsx3. +% Column 1: Start sample for trials +% Column 2: End sample for trials +% Column 3: Offset of beginning of each trial from Trigger +% onset in Samples. For this specific trial function it is +% always 0. +% Example: +% +% cfg=[]; +% cfg.dataset='here goes the name of the raw data file from the MEG scan '; +% cfg.trialfun='trialfun_Restin'; +% cfg.trialdef.trialDuration=1; +% cfgDefTr = ft_definetrial(cfg); +% cfgDefTr.dataformat='4d'; +% cfgDefTr.headerformat='4d'; +% dataRaw = ft_preprocessing(cfgDefTr); + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +%=========================================================== + datafile=inputCfg.dataset; + trialDuration=inputCfg.trialdef.trialDuration; + hdr = ft_read_header(datafile,'headerformat','4d'); + Nsamples=hdr.nSamples; + Fsample=hdr.Fs; + trialSamples=floor(trialDuration*Fsample); + + tmpTrialRef=1:trialSamples:Nsamples; + startSamples=tmpTrialRef(1:end-1); + endSamples=tmpTrialRef(2:end)-1; + + trl=[startSamples' endSamples' repmat(0,length(startSamples),1)]; + %=========================================================== + diff --git a/trial_functions/trialfun_StoryM.m b/trial_functions/trialfun_StoryM.m new file mode 100644 index 0000000..05eaafd --- /dev/null +++ b/trial_functions/trialfun_StoryM.m @@ -0,0 +1,22 @@ +function [trl,trlInfoColDescr, trialSummary, scanStartSamp, scanEndSamp, warninfo] = trialfun_StoryM( cfg ) + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +[trl,trlInfoColDescr, trialSummary, scanStartSamp, scanEndSamp, warninfo] = trialfun_StoryM_BaseExtractAll( cfg ); + +end diff --git a/trial_functions/trialfun_StoryM_BaseExtractAll.m b/trial_functions/trialfun_StoryM_BaseExtractAll.m new file mode 100644 index 0000000..ed3dda9 --- /dev/null +++ b/trial_functions/trialfun_StoryM_BaseExtractAll.m @@ -0,0 +1,728 @@ +function [trl,trlInfoColDescr, trialSummary,scanStartSamp,scanEndSamp,warninfo] = trialfun_StoryM_BaseExtractAll( cfg ) + +% This is the Trial Definition Function for Story/Math Task experiment. +% The trigger is derived from ONLY Parallel Port transients on the trigger channle +% This function aims at extracting ALL the trial definitions for a given +% data group definition. The conditions within each data group are then +% extracted by the corresponding contrast function. +%__________________________________________________________________________ +%cfg.datafile : The datafile to read the triggers from +%cfg.trialdef.cutmode %1. All Events - Onset of Story Sentences, Math number words, Math operand words, Option intro word,Option 1, Option OR, Option 2 (Fixed trial length) +% %2. Response (Fixed trial length) +% %3. Sentences (Story Sentences or Math question sentences(not including option interval)) (Variable trial length) +% %4. Units (Stories or Math Problems including Option Interval) (Variable trial length) +% +% cfg.trialdef.prestimTime: Desired time interval (in seconds) before Trigger onset. Must be positive. +% For both of events and blocks, prestimTime will be considered. +% cfg.trialdef.poststimTime: Desired time interval (in seconds) after Trigger onset. Must be positive. +% For both of events and blocks, poststimTime will be considered. +% cfg.trialdef.plotresults = 'yes' or 'no' (default = 'no') +%__________________________________________________________________________ +% Output: +% trl: Ntrials-by-3 matrix with the trial definition +% Column 1: Start sample for trials +% Column 2: End sample for trials +% Column 3: Offset of beginning of each trial from Trigger onset in Samples (i.e. -100). +% Column 4 to end : Additional information about the trials +%__________________________________________________________________________ +%% === Example: +% cfg = []; +% cfg.datafile = 'xxx\c,rfDC';% xxx is directory of the raw MEG scan +% cfg.trialfun = 'trialfun_StoryM_BaseExtractAll'; +% cfg.trialdef.cutmode = 1 % Beginning of Narration Unit (Sentence or Math Problem) (Fixed trial length) +% cfg.trialdef.prestimTime = 1.5; +% cfg.trialdef.poststimTime =4; +% %cfg.trialdef.plotresults = 'no';%(default = 'no') +% cfgDefTr = ft_definetrial(cfg); +% cfgDefTr.dataformat = '4d'; +% cfgDefTr.headerformat = '4d'; +% dataRaw = ft_preprocessing(cfgDefTr); + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + +%__________________________________________________________________________ + +if ~isfield(cfg.trialdef, 'plotresults'), cfg.trialdef.plotresults = 'no'; end + +%% =========== Read cfg info +datafile = cfg.datafile; +cutmode=cfg.trialdef.cutmode; +prestimTime = cfg.trialdef.prestimTime; +poststimTime = cfg.trialdef.poststimTime; +if strcmpi(cfg.trialdef.plotresults , 'yes' ), plot_flag = 1; else plot_flag = 0; end + +%% +hdr = read_header(datafile); +Fsample = hdr.Fs; +prestimSamples = floor(prestimTime*Fsample); +poststimSamples = floor(poststimTime*Fsample); + +detTrig=ft_read_data(datafile,'chanindx',[1],'header',hdr,'eventformat','4d','dataformat','4d'); +detTrig=bitand(detTrig,255); +detResp=ft_read_data(datafile,'chanindx',[2],'header',hdr,'eventformat','4d','dataformat','4d'); +Nsamps=length(detTrig); + + + +%--------------------------------------- + +%% TRIGGERS +%========================================== +%-- Trigger definitions from the Eprime +EP=[]; +EP.TrigCodStroryMathNumLevel=128; +EP.TrigCodStoryCue=96; +EP.TrigCodMathCue=112; +EP.TrigCodStoryBase=32; +EP.TrigCodStoryTrigStart=24; +EP.TrigCodStoryTrig=26; +EP.TrigCodStoryEndWait=16; +EP.TrigCodStoryThat=14; +EP.TrigCodStoryOptUnCor=12; +EP.TrigCodStoryOptCor=10; +EP.TrigCodStoryOR=8; +EP.TrigCodStoryResp=2; +EP.TrigCodMathBase=64; +EP.TrigCodMathQuestBlock=16; +EP.TrigCodMathQuestTrig=8; +EP.TrigCodMathEqualBlockUnCor=8; +EP.TrigCodMathEqualBlockCor=0; +EP.TrigCodMathEqualTrig=4; +EP.TrigCodMathResp=2; +%========================================== + +%========================================== +%-- Form Triggers +trg_headblock=128; % Before each math or story there is header trigger with some superimposed triggers +% For story there are 2 superimposed triggers. The first +% is the story id *2. The second? +% For math there are 3 superimposed triggers. The first +% is the math difficulty level*2. The other two triggers? + +trgST_Cue=EP.TrigCodStoryCue; % These 2 cue triggers are sent everytime a change between a story and math +% or vice versa takes place, so they signal +% the big blocks appart from the first one +trgMA_Cue=EP.TrigCodMathCue; +%------------------------------------------------------------- + +trgST_start=EP.TrigCodStoryBase+EP.TrigCodStoryTrigStart; +trgST_senton=EP.TrigCodStoryBase+EP.TrigCodStoryTrig; +trgST_endwait=EP.TrigCodStoryBase+EP.TrigCodStoryEndWait;% There is a 500 msec wait after the end of the last sentence? This represents the end of this 500 msec? +trgST_that=EP.TrigCodStoryBase+EP.TrigCodStoryThat; % This is the beginning of the sentence "That story was about" +trgST_OptCor=EP.TrigCodStoryBase+EP.TrigCodStoryOptCor; % This is the correct option; +trgST_OptUnCor=EP.TrigCodStoryBase+EP.TrigCodStoryOptUnCor; % This is the uncorrect option; +trgST_RespBlock=EP.TrigCodStoryBase+EP.TrigCodStoryResp; % This is trigger of the Response Block +trgST_OptOR=EP.TrigCodStoryBase+EP.TrigCodStoryOR; +%-------------------------------------------------------------- +trgMA_N_base=EP.TrigCodMathBase+EP.TrigCodMathQuestBlock; %This is the basis trigger in the Math Narration interval upon which the triggers for inidividual words ans numbers are superimposed +trgMA_N_wordon=EP.TrigCodMathBase+EP.TrigCodMathQuestBlock+EP.TrigCodMathQuestTrig; % Math: Narration block: onset of number? There are +% 7 of those and some time after the last one the +% Narration block ends end trigger falls. So it seems +% like the triggers correspond to 4 numbers being +% participating in the addition/subtraction. i.e. +% one plus two plus three minus four + +trgMA_O_Cor1=EP.TrigCodMathBase+EP.TrigCodMathEqualBlockCor; % After the narration block is the option block. The base trigger for +% this block +% is different if the 1st or the second option is the correct one. +% The triggers for the words "equals" option1 "or" option2 +% are then superimposed on this Base trigger +trgMA_O_Cor2=EP.TrigCodMathBase+EP.TrigCodMathEqualBlockUnCor; +trgMA_O_Cor1wordon=trgMA_O_Cor1+EP.TrigCodMathEqualTrig; % This is the trigger for words "equals" option1 "or" option2 +trgMA_O_Cor2wordon=trgMA_O_Cor2+EP.TrigCodMathEqualTrig; +trgMA_RespBlock=EP.TrigCodMathBase+EP.TrigCodMathResp; % This is trigger of the Response Block + +durTimeResp=3; +durSampResp=floor(durTimeResp*Fsample); +%--------------------------------------------------------------- +% - Left Button should be the response for Option 1 +% and Right Button should be the response for Option 2 ??? +respLeft=256; +respRight=512; +%------------------------------------------- + +%% create indices of up down of trigger and response. Find indices of block changes and header interval onset +difTrig=diff(detTrig); +indxTrigUp=find(difTrig>0)+1; +indxTrigDown=find(difTrig<0)+1; +indxTrigUpDown=find(difTrig~=0)+1; + +difResp=diff(detResp); +indxRespUp=find(difResp>0)+1; +indxRespDown=find(difResp<0)+1; + +%===================================================== +%===================================================== +%===================================================== +warninfo=[]; +%-Identify Story and Math Blocks and make +[i1,j1]=find(ismember(detTrig(2:end),[trgST_Cue trgMA_Cue])&(difTrig>0)); +indxBlockChange=j1+1; +if (length(indxBlockChange))~=7, + disp(['WARNING!!! - It seems that there are NOT 8 blocks in your story math raw data. ']); +end +indxHeadOn=find((detTrig(2:end)==trg_headblock)&(difTrig>0))+1; +Nblocks=length(indxBlockChange)+1; + +%----------------------------------------------------------------- + +%% Create trlBlockInfo +%----------------------------- +% Create trlBlockInfo +% COLUMNS: +% 1. Block Type : 1.Story 2.Math +% 2. Block Start Sample +% 3. Block End Sample +% 4. Number of units (N of Stories or N of Math problems ) in Block + +% Define as the beginning of the block the onset of the first header block +% within the block +% Define the end of each block as the sample before the next block change signal (for the last block is the last sample) +trlBlockInfo=nan(Nblocks,4); +for iBlock=Nblocks-1:-1:1, + trlBlockInfo(iBlock+1,1)=find(detTrig(indxBlockChange(iBlock))==[trgST_Cue trgMA_Cue]); % 1.Story 2.Math + trlBlockInfo(iBlock+1,2)=indxHeadOn(find(indxHeadOn>indxBlockChange(iBlock),1,'first')); + if iBlock==(Nblocks-1), + + indRespLast=indxTrigUpDown(find(ismember(detTrig(indxTrigUpDown),[trgMA_RespBlock trgST_RespBlock]),1,'last')); + trlBlockInfo(iBlock+1,3)=indRespLast+durSampResp; + else + trlBlockInfo(iBlock+1,3)=indxBlockChange(iBlock+1)-1; + end +end +if trlBlockInfo(2,1)==trgST_Cue + trlBlockInfo(1,1)=2; +else + trlBlockInfo(1,1)=1; +end +trlBlockInfo(1,2)=indxHeadOn(find(indxHeadOn>1,1,'first')); +trlBlockInfo(1,3)=indxBlockChange(1)-1; +%----------------------------- +% Find how many units (N of Stories or N of Math problems ) are in each +% block +for iBlock=1:Nblocks, + trlBlockInfo(iBlock,4)=sum((indxHeadOn>=trlBlockInfo(iBlock,2))&(indxHeadOnNsamps, + scanEndSamp=Nsamps; +end +%====================================================== + + + +%figure;hold on; +%plot(detTrig); +%plot(trlBlockInfo(:,2),detTrig(trlBlockInfo(:,2)),'.g'); +%plot(trlBlockInfo(:,3),detTrig(trlBlockInfo(:,3)),'.r'); + +%-------------------------------------------------------- + + +%% Create general trlUnitInfo +%===================================================== +%===================================================== +%===================================================== +%===================================================== +% Create general trlUnitInfo +% Columns: +%---------------------------------------------- +trlColDescr_UnitInfo={'1. Block Number within Run' +'2. Unit Type : 1.Story 2.Math' +'3. Unit Number within Run' +'4. Total Number of units (N of Stories or N of Math problems ) in same Run' +'5. Unit Number within Block' +'6. Total Number of units (N of Stories or N of Math problems ) in same Block' +'7. Attribute1: For story this is the story number. For Math is the difficulty level' +'8. Unit Narration interval Start Sample - Start with the onset of the first word trigger or the beginning of the first sentence' +'9. Unit Narration interval End Sample' +'10. N subunits within Narration interval' +'11. Unit Option interval Start Sample' +'12. Unit Option interval End Sample' +'13. N subunits within Option interval' +'14. Option Intro Sample - "equals" or "That was about"' +'15. Option1 onset sample' +'16. OR onset sample' +'17. Option2 onset sample' +'18. Correct Option- 1 or 2' +'19. Unit Response interval start sample' +'20. Unit Response interval end sample' +'21. Unit Response sample' +'22. is Response Correct' +'23. is Response Early'}; + +Ntotalun=sum(trlBlockInfo(:,4)); +trlUnitInfo=nan(Ntotalun,22); +countUn=1; +for iBlock=1:Nblocks, + tmpNun=trlBlockInfo(iBlock,4); + + tmpIndxHeadOn=indxHeadOn(find((indxHeadOn>=trlBlockInfo(iBlock,2))&(indxHeadOntmpIndxHeadOn(iUn)),1,'first'))) - trg_headblock); + + if trlUnitInfo(countUn,2)==1 % Story + trlUnitInfo(countUn,8)=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_senton),1,'first')); + trlUnitInfo(countUn,9)=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_endwait),1,'first'))-1; + trlUnitInfo(countUn,10)=length(find((indxTrigUp>=trlUnitInfo(countUn,8))&(indxTrigUptmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_that),1,'first')); + trlUnitInfo(countUn,12)=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_RespBlock),1,'first'))-1; + trlUnitInfo(countUn,13)=length(find((indxTrigUp>=trlUnitInfo(countUn,11))&(indxTrigUptmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_that),1,'first')); + tmpCorIndx=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_OptCor),1,'first')); + tmpUnCorIndx=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_OptUnCor),1,'first')); + if tmpCorIndx>tmpUnCorIndx + trlUnitInfo(countUn,15)=tmpUnCorIndx; + trlUnitInfo(countUn,17)=tmpCorIndx; + trlUnitInfo(countUn,18)=2; + else + trlUnitInfo(countUn,15)=tmpCorIndx; + trlUnitInfo(countUn,17)=tmpUnCorIndx; + trlUnitInfo(countUn,18)=1; + end + trlUnitInfo(countUn,16)=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgST_OptOR),1,'first')); + + + + else % Math + + trlUnitInfo(countUn,8)=indxTrigUp(find((indxTrigUp>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==trgMA_N_wordon),1,'first')); + trlUnitInfo(countUn,9)=indxTrigDown(find((indxTrigDown>trlUnitInfo(countUn,8))&(detTrig(indxTrigDown-1)==trgMA_N_base),1,'first')); + trlUnitInfo(countUn,10)=length(find((indxTrigUp>=trlUnitInfo(countUn,8))&(indxTrigUptmpIndxHeadOn(iUn))&(detTrig(indxTrigUp)==tmp_O_wordon),1,'first')); + trlUnitInfo(countUn,12)=indxTrigUpDown(find((indxTrigUpDown>tmpIndxHeadOn(iUn))&(detTrig(indxTrigUpDown)==trgMA_RespBlock),1,'first'))-1; + tmp_O_WordIndx=indxTrigUp(find((indxTrigUp>=trlUnitInfo(countUn,11))&(indxTrigUptrlUnitInfo(countUn,15))&(detTrig(indxTrigDown)==0),1,'first'))-1; + else + indRespLast=indxTrigUpDown(find(ismember(detTrig(indxTrigUpDown),[trgMA_RespBlock trgST_RespBlock]),1,'last')); + trlUnitInfo(countUn,20)=indRespLast+durSampResp; + end + %----------------------------- + % Check SUbject's Response + isRespEarly=0; + tmpRespIndx=indxRespUp(find((indxRespUp>trlUnitInfo(countUn,19))&(indxRespUp<=trlUnitInfo(countUn,20)),1,'first')); + if ~isempty(tmpRespIndx), + trlUnitInfo(countUn,21)=tmpRespIndx; + if ((detResp(tmpRespIndx)==respLeft)&(trlUnitInfo(countUn,18)==1))|((detResp(tmpRespIndx)==respRight)&(trlUnitInfo(countUn,18)==2)) + trlUnitInfo(countUn,22)=1; + else + trlUnitInfo(countUn,22)=0; + end + else + if any(ismember([respLeft,respRight],detResp(trlUnitInfo(countUn,19)))) + isRespEarly=1; + if ((detResp(trlUnitInfo(countUn,19))==respLeft)&(trlUnitInfo(countUn,18)==1))|((detResp(trlUnitInfo(countUn,19))==respRight)&(trlUnitInfo(countUn,18)==2)) + trlUnitInfo(countUn,22)=1; + else + trlUnitInfo(countUn,22)=0; + end + tmpRespIndx=indxRespUp(find(indxRespUp<=trlUnitInfo(countUn,19),1,'last')); + trlUnitInfo(countUn,21)=tmpRespIndx; + end + end + + trlUnitInfo(countUn,23)=isRespEarly; + countUn=countUn+1; + end +end +%===================================================== +%===================================================== +%===================================================== +%===================================================== + +%% Create detailed trlSubUnitInfo for +%===================================================== +% Create detailed trlSubUnitInfo for +% Columns: +%---------------------------------------------- +% 1. Block Number within Run +% 2. Unit Type : 1.Story 2.Math +% 3. Unit Number within Run +% 4. Total Number of units (N of Stories or N of Math problems ) in same Run +% 5. Unit Number within Block +% 6. Total Number of units (N of Stories or N of Math problems ) in same Block +% 7. Attribute1: For story this is the story number. For Math is the +% difficulty level +% 8. Unit Narration interval Start Sample - Start with the onset of the +% first word trigger or the beginning of the first sentence +% 9. Unit Narration interval End Sample +% 10. N subunits within Narration interval +% 11. Unit Option interval Start Sample +% 12. Unit Option interval End Sample +% 13. N subunits within Option interval +% 14. Option Intro Sample - "equals" or "That was about" +% 15. Option1 onset sample +% 16. OR onset sample +% 17. Option2 onset sample +% 18. Correct Option- 1 or 2 +% 19. Unit Response interval start sample +% 20. Unit Response interval end sample +% 21. Unit Response sample +% 22. is Response Correct +% 23. is Response Early +% 24. Narration Subunit Number in Narration interval (Story Sentence or (Math Number or operation)) +% 25. Narration Subunit Sample +% 26. Narration Subunit Type: 1:Language (Sentence or math operation word i.e. plus) 2: Math Number + +countsubs=1; +trlSubUnitInfo=[]; +for iUn=1:Ntotalun, + + tmpNsubs=trlUnitInfo(iUn,10); + + + if trlUnitInfo(iUn,2)==1%Story + tmpIndxSubs=find((indxTrigUp>=trlUnitInfo(iUn,8))&(indxTrigUp=trlUnitInfo(iUn,8))&(indxTrigUp. + +[trl,trlInfoColDescr, trialSummary, scanStartSamp, scanEndSamp, warninfo] = trialfun_Wrkmem_BaseExtractAll( cfg ); + +end diff --git a/trial_functions/trialfun_Wrkmem_BaseExtractAll.m b/trial_functions/trialfun_Wrkmem_BaseExtractAll.m new file mode 100644 index 0000000..3c8210f --- /dev/null +++ b/trial_functions/trialfun_Wrkmem_BaseExtractAll.m @@ -0,0 +1,823 @@ +function [trl,trlInfoColDescr,trialSummary,scanStartSamp,scanEndSamp,warninfo]= trialfun_Wrkmem_BaseExtractAll( cfg ) + +% This is the Trial Definition Function for Working Memomory experiment. +% The trigger is derived from the PhotoDiode & Parallel Port transients on the trigger channle. +% +% FIXME !!!! everything is derived from parallel port trigger at the moment !!!! +%_ +% This function extract information about all trials +% Input: +% cfg: Structure with fields that contain parameters for extracting trials +% cfg.datafile: Char string represents filename of raw input MEG data +% cfg.trialdef.cutMode = 'trials','blocks' or 'fixation' (default = 'trials') +% 'trials': Each trial corresponding to each stimulus is cut +% separately. Fixation blocks are chopped in equal length trials and +% appended at the end. +% 'blocks': The data is cut in the actual stimuli blocks. Each block has 10 trials. +% 'fixation': Only the fixation blocks +% +% cfg.trialdef.lockedOn = 'Image' or 'Response' (default = 'Image') +% [Will be used if only .trialdef.cutMode == 'trials'] +% 'Image': Trial based on the ONSET of image presentation +% 'Response': Trial based on the subject's response (key pressed) time +% +% cfg.trialdef.prestimTime: Desired time interval before Trigger onset - in seconds. Must be positive. +% If .cutMode=='trials', then this is the time before the trial ONSET trigger. +% If .cutMode=='blocks', then this is the time before the block ONSET trigger. +% If .cutMode=='fixation', then this is the time before the fixation ONSET trigger. +% +% cfg.trialdef.poststimTime: Desired time interval after Trigger onset - in seconds. Must be positive. +% If .cutMode=='trials', then this is the time after the trial ONSET trigger. +% If .cutMode=='blocks', then this is the time after the block OFFSET trigger. It adds a timing pad after the end of block. +% If .cutMode=='fixation', then this is the time after the fixation OFFSET trigger. It adds a timing pad after the end of fixation. +% +% cfg.trialdef.plotresults = 'yes' or 'no' (default = 'no') +% +% FIXME !!!! The following option in not working yet !!!! +% ---------------------------------------------------------- +% cfg.badsegment: Bad segment of data. Trigger will be forced to zero in bad segments. For example, actual recording for "Wrkmem_MEB_V2" was started after 78 seconds. +% ---------------------------------------------------------- +% +% Output: +% The first 3 columns of the outputmatrix trl are the typical Fieldtrip trl +% matrix with first column the beginning of trial , the second the end of +% trial and the third the offset of the beginning of trial from trigger. + +% Copyright (C) 2011-2013 by the Human Connectome Project, WU-Minn Consortium (1U54MH091657) +% +% This file is part of megconnectome. +% +% megconnectome is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% megconnectome is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with megconnectome. If not, see . + + +% The remaining 39 columns are: +%===== COLUMNS: +trlColDescr={'1. Run Number' +'2. Block Number' +'3. Image Index in the list of all images.Not yet available. TODO: Add it here' +'4. imgType : 1- Face, 2- Tools 0- Fixation' +'5. memoryType : 1: 0-Back 2: 2-Back' +'6. targetType : 1- target, 2- nontarget, 3: lure ' +'7. Trial trigger onset Sample ' +'8. Trial trigger offset Sample' +'9. Sequence of image in the block' +'10. isPressed : 0- user did not press any response button, 1- user pressed a response button' +'11. isPressedLate: 1- If subject responded after the 2 seconds that the image is at the longest displayed and before the next trial \n 0- If pressed within the presentation time of the image\n , NaN: Otherwise' +'12. isDoubleResponse: 1- user pressed two response buttons in the same trial \n 0: user DID NOT press two response buttons in the same trial' +'13. pressedCode: Code of the pressed button (If not pressed NaN)' +'14. isCorrect: 1- If subject has responded that saw a target when a actual target was on or that saw a nontarget when a actual nontarget was on \n 0: The opposite of the above.\n NaN: When subject has not responded or has pressed two buttons' +'15. isLureAsCorrect: 1- If subject has responded that saw a target when a lure image of actual target was on \n , 0: In all other cases that a subject has responded\n NaN: When subject has not responded or has pressed two buttons' +'16. respTime: The number of samples from onset of Image to response' +'17. respDuration: Duration of button press in seconds' +'18. isFirstInBlock' +'19. isLastInBlockk' +'20. prev. Trial: Run Number' +'21. prev. Trial: Block Number' +'22. prev. Trial: Image Index in the list of all images.Not yet available. TODO: Add it here' +'23. prev. Block: imgType : 1- Face, 2-Tools 0- Fixation' +'24. prev. Block: memoryType : 1: 0-Back 2: 2-Back' +'25. prev. Trial: targetType : 1- target, 2- nontarget, 3: lure ' +'26. prev. Trial: Trial start Sample' +'27. prev. Trial: Trial end Sample' +'28. prev. Trial: Sequence of image in the block' +'29. prev. Trial: isPressed : 0- user did not press any response button, 1- user pressed a response button' +'30. prev. Trial: isPressedLate: 1- If subject responded after the 2 seconds that the image is at the longest displayed and before the next trial \n 0- If pressed within the presentation time of the image\n , NaN: Otherwise' +'31. prev. Trial: isDoubleResponse: 1- user pressed two response buttons in the same trial \n 0: user DID NOT press two response buttons in the same trial' +'32. prev. Trial: pressedCode: Code of the pressed button (If not pressed NaN)' +'33. prev. Trial: isCorrect: 1- If subject has responded that saw a target when a actual target was on or that saw a nontarget when a actual nontarget was on \n 0: The opposite of the above.\n NaN: When subject has not responded or has pressed two buttons' +'34. prev. Trial:isLureAsCorrect: 1- If subject has responded that saw a target when a lure image of actual target was on \n , 0: In all other cases that a subject has responded\n NaN: When subject has not responded or has pressed two buttons' +'35. prev. Trial: respTime: The number of samples from onset of Image to response' +'36. prev. Trial: respDuration: Duration of button press in seconds' +'37. prev. Trial: isFirstInBlock' +'38. prev. Trial: isLastInBlock' +'39. Is button pressed during onset of the stimulus (New field)'}; +%========================================================================= +blockColDescr={''}; % TODO: IF DATA ARE TO BE EXTRACTED IN BLOCKS THEN FORM THE DESCRIPTION OF THE BLOCKS +%========================================================================= +%% INPUTS + + +datafile=cfg.datafile; +preStimTime=cfg.trialdef.preStimTime; +postStimTime=cfg.trialdef.postStimTime; +cutMode=cfg.trialdef.cutMode; +lockedOn=cfg.trialdef.lockedOn; + +idealISItime=2.5; % This is used to cut Fixation in a similar fashion as the stim blocks +%========================================================================== +%% Read the data file +hdr = read_header(datafile); +Fsample = hdr.Fs; +trg_resp = ft_read_data(datafile,'chanindx',[1 2],'header',hdr,'eventformat','4d','dataformat','4d'); + +preStimSamples=floor(Fsample*preStimTime); +postStimSamples=floor(Fsample*postStimTime); +idealISIsamps=floor(Fsample*idealISItime); +%========================================================================= +%% TRIGGER DEFINITIONS + +trgMix = trg_resp(1,:); +Nsamples=length(trgMix); + +resp = trg_resp(2,:); +% correct possible wrong negative values in response. (Not sure which case this refers to) +ixn = find( resp < 0 ); +resp(ixn) = typecast( int16( resp(ixn) ) , 'uint16' ); +% Index Finger (Match to target): Response Code = 256 ('01 0000 0000') +% Middle Finger (NonMatch to target): Response Code = 512 ('10 0000 0000') +resp = bitand( resp , bin2dec( '11 0000 0000' ) ); % This ensures that random onset of the most significant bit are removed. + +%% +%========================================== +% Separate Photo diode and parallel port triggers +% PhotoDiode only +%-------------------- +% Photodiode can take values only 256 ('1 0000 0000') and 0. +trigPhoto = bitand( trgMix , bin2dec('1 0000 0000') ); % This ensures that random onset of the most significant bit are removed. + +%Parallel Port Only +%-------------------- +% Parallel port cannot take values outside range 2 (0 0000 0010) and 254 ('0 1111 1110') and 0. +trigPP= bitand(trgMix , bin2dec('0 1111 1110') ); % This ensures that random onset of the most significant bit are removed. + +%% +%==================================================== +%% Get Events indices in both photodiode and parallel port triggers +difTrigPP=diff(trigPP); +difTrigPhoto=diff(trigPhoto); + +indPP_UP=find(difTrigPP>0)+1; +indPP_DOWN=find(difTrigPP<0)+1; +indPP_UPDOWN=find(difTrigPP~=0)+1; + +indPhoto_UP=find(difTrigPhoto>0)+1; +indPhoto_DOWN=find(difTrigPhoto<0)+1; +indPhoto_UPDOWN=find(difTrigPhoto~=0)+1; + + +%---------------------------------------------------------------- +%============================================================== +%% +%-------------------------------- +% For some scans the photodiode is on at the beginning or/and end of the scan +% So this initial and last interval must not be used in the trial +% extraction + +if trigPhoto(1)>0 + trigPhoto(1:indPhoto_DOWN(1)-1)=0; + difTrigPhoto=diff(trigPhoto); + [indPhoto_UP]=find((difTrigPhoto>0))+1; + [indPhoto_DOWN]=find((difTrigPhoto<0))+1; + [indPhoto_UPDOWN]=find((difTrigPhoto~=0))+1; +end +if trigPhoto(end)>0 + trigPhoto(indPhoto_UP(end):end)=0; + difTrigPhoto=diff(trigPhoto); + [indPhoto_UP]=find((difTrigPhoto>0))+1; + [indPhoto_DOWN]=find((difTrigPhoto<0))+1; + [indPhoto_UPDOWN]=find((difTrigPhoto~=0))+1; +end + +%--------------------------------- +%=============================================================== +%% First Decode Parallel Port Trigger Events + +memTypeTrig=[ 4 % 0-Back + 68 % 2-Back + 2]; % Fixation + +imgTypeTrigStep=[ 4 % Face + 36]; % Tool + +targTypeTrigStep=[2 % Non-target + 4 % Lure + 6]; % Target +durTimeCue=2.5; +durTimeImg=2; +durTimeImgFix=0.5; +durTimeFix15=15; +durSampCue=floor(durTimeCue*Fsample); +durSampImg=floor(durTimeImg*Fsample); +durSampImgFix=floor(durTimeImgFix*Fsample); +durSampFix15=floor(durTimeFix15*Fsample); + + +% The decoding is based on the following trigger protocol used by e-prime +% Cue on trigger : memTypeTrig +% Img on trigger : memTypeTrig+imgTypeTrigStep+targTypeTrigStep +% Img off trigger : memTypeTrig+imgTypeTrigStep + + + +%{ +trlColDescr={ + +%} +NeventsPP=length(indPP_UPDOWN); +eventPPInfo=nan(NeventsPP,6); +%{ +This structure contains information about all the events in the Parallel +Port +Columns: +'1. event Type: 1. Cue 2. Img On 3. Img Off 4.Fixation 15sec 5. Intro On 6. Intro off +'2. imgType : 1- Face, 2- Place, 3- Body 4- Tools 0- Fixation' +'3. memoryType : 1: 0-Back 2: 2-Back 0-Fixation' +'4. targetType : 1- target, 2- nontarget, 3: lure 0-Cue or Fixation' +'5. Event Sample from PP' +'6. Event Sample from PhotoDiode +%} + + +%% +for iMem=1:length(memTypeTrig), %[ 4 % 0-Back 68 % 2-Back 2]; % Fixation + + indIn=find(ismember(trigPP(indPP_UPDOWN),memTypeTrig(iMem))); + %----------------------------------------------------- + if isempty(indIn) + error('No triggers for Working Memory Cue were found'); + else + if iMem==3 + eventPPInfo(indIn,1)=4; + eventPPInfo(indIn,2:4)=0; + else + eventPPInfo(indIn,1)=1; + eventPPInfo(indIn,3)=iMem; + eventPPInfo(indIn,4)=0; + end + eventPPInfo(indIn,5)=indPP_UPDOWN(indIn); + end + %----------------------------------------------------- + + if iMem<3, + + tmpImgTypeTrigStep=memTypeTrig(iMem)+imgTypeTrigStep; + + for iImg=1:length(tmpImgTypeTrigStep), %memTypeTrig(iMem)+[ 4 % Face 36]; % Tool + indIn=find(ismember(trigPP(indPP_UPDOWN),tmpImgTypeTrigStep(iImg))); + %----------------------------------------------------- + if isempty(indIn) + error('No triggers for Image off Cue were found'); + else + eventPPInfo(indIn,1)=3; + eventPPInfo(indIn,3)=iMem; + eventPPInfo(indIn,2)=iImg; + eventPPInfo(indIn,5)=indPP_UPDOWN(indIn); + end + %----------------------------------------------------- + tmpTargTypeTrigStep=tmpImgTypeTrigStep(iImg)+targTypeTrigStep; + for iTarg=1:length(tmpTargTypeTrigStep), %[2 % Non-target 4 % Lure 6]; % Target + indIn=find(ismember(trigPP(indPP_UPDOWN),tmpTargTypeTrigStep(iTarg))); + %----------------------------------------------------- + if isempty(indIn) + error('No triggers for Image On Cue were found'); + else + eventPPInfo(indIn,1)=2; + eventPPInfo(indIn,3)=iMem; + eventPPInfo(indIn,2)=iImg; + if iTarg==1, + eventPPInfo(indIn,4)=2; + elseif iTarg==2, + eventPPInfo(indIn,4)=3; + elseif iTarg==3 + eventPPInfo(indIn,4)=1; + end + eventPPInfo(indIn,5)=indPP_UPDOWN(indIn); + end + %----------------------------------------------------- + end + end + + end +end + +%====================================================================== +% Fill in +% - Image Type for Cue events +% - Target Type for Image Off Events +for iEv=1:NeventsPP-1, + %----------------------- + if ((eventPPInfo(iEv,1)==1)&(eventPPInfo(iEv+1,1)==2)), + eventPPInfo(iEv,2)=eventPPInfo(iEv+1,2); + end + %----------------------- +end +for iEv=2:NeventsPP, + %----------------------- + if ((eventPPInfo(iEv,1)==3)&(eventPPInfo(iEv-1,1)==2)), + eventPPInfo(iEv,4)=eventPPInfo(iEv-1,4); + end + %----------------------- +end +%====================================================================== +%% DO consistency checks on the triggers + +warninfo=[]; +NeventsPhoto=length(indPhoto_UPDOWN); +rowsPPImg=find(ismember(eventPPInfo(:,1),[2 3]))'; +NImgEventsPP=length(rowsPPImg); +indPPImg_UPDOWN=eventPPInfo(rowsPPImg,5)'; + +if NeventsPhoto~=NImgEventsPP, + warninfo.dif_ph2pp=[]; + tmpDifP=diff(indPhoto_UPDOWN); + difThresh=floor((8/60).*Fsample); % 8 monitors refresh frames + indRand=find(tmpDifP (2/60) + warning('The avergae difference between Photodiode and Parallel Port Triggers is more than 2 monitor refresh rates.'); + warninfo.dif_ph2pp.avgdelay=mean(abs(tmpDifPhoto2PP)./Fsample); +end + +indBad=find(abs(tmpDifPhoto2PP)>difThresh); +if ~isempty(indBad) + warning('For some image onset or offset event the difference between Photodiode and Parallel Port Triggers are more than 6 monitor refresh rates.'); + warninfo.dif_ph2pp.samp_longdelay=indPhoto_UPDOWN(indBad); +end +eventPPInfo(rowsPPImg,6)=indPhoto_UPDOWN; +%----- + +nanIndx=find(isnan(eventPPInfo(:,1)))'; +difNanIndx=diff(nanIndx); + +if length(nanIndx)>2 + error('More than 2 PP events remained non assigned'); +end +if length(nanIndx)==0 + error('No PP events remained non assigned. There should be one or two at the beginning for the introductory screen and the countdown'); +end +if length(find(difNanIndx>1)) + error('The unassigned PP events are not adjacent') +end +if max(nanIndx)>2 + error('Only the first 2 PP events at max can be unassigned') +end + +eventPPInfo(nanIndx,5)=indPP_UPDOWN(nanIndx);%Just fill in the onset sample of unassigned events + +if length(nanIndx)==1, % This must be a downward trigger event + if nanIndx~=1, + error('There is one unassigned event , but is not the first one as expected'); + else + if difTrigPP(eventPPInfo(nanIndx,5)-1)>=0 + error('There is one unassigned event at the beginning but the trigger is not down going') ; + end + end +elseif length(nanIndx)==2 + if ~all(nanIndx==[1 2]) + error('There are two unassigned events , but are not the first two as expected'); + else + if ~((difTrigPP(indPP_UPDOWN(nanIndx(1))-1)>0)&(difTrigPP(indPP_UPDOWN(nanIndx(2))-1)<0)) + warning('There are two unassigned events at the beginning but the triggers are not up going and then down going') ; + warninfo.pp.samp_initTrigNotUpDown=[indPP_UPDOWN(nanIndx(1)) indPP_UPDOWN(nanIndx(2))]; + end + end +end + +if eventPPInfo(end,1)~=4 + error('The last event should be the start of the last fixation block'); +else + if (eventPPInfo(end,5)+durSampFix15)>Nsamples, + error('The last fixation block is not as long as expected(15sec). Scan ends earlier.'); + end +end + +%====================================== +%% If all consistency tests are passed try to read the run number +runNumber=0; +if length(nanIndx)==1, % This must be a downward trigger event + tmpRunTrig=trigPP(indPP_UPDOWN(nanIndx(1))-1); +elseif length(nanIndx)==2 + tmpRunTrig=trigPP(indPP_UPDOWN(nanIndx(2))-1); +end +if tmpRunTrig==130, + runNumber=1; +elseif tmpRunTrig==132, + runNumber=2; +end +%====================================================================== +%% If the code has reached this point with no error then all PP event information must have been decoded, +% and the Image Onset and Offest triggers must be matched between PP and +% Photodiode. No photodiode triggers are available for the cues or for the +% Fixation. So for the decoding, the trigger samples for Cues and Fixation +% blocks will extracted from the PP while the trigger samples for the Image +% onsets will be recovered from the Photodiode triggers. + +% FIND BLOCK ON BLOCK OFF - Block starts a the onset of a cue and on sample +% before the onset of the next cue. For the last fixation block the +% end is defined by the default duration of a fixation block. +rowsCueOrFix=find(ismember(eventPPInfo(:,1),[1 4])); +blockPPInfo=eventPPInfo(rowsCueOrFix,:); + +indxBlockOn=blockPPInfo(:,5); +indxBlockOff=blockPPInfo(2:end,5)-1; +indxBlockOff(end+1)=indxBlockOn(end)+durSampFix15; + +Nblocks=length(indxBlockOn); +%====================================================== +%% Identify the start and end of scan as the onset of the first block and the end of the last block +% A default temporal padding of 3 second is used in either end +defEndPad=floor(3*Fsample); +scanStartSamp=indxBlockOn(1)-defEndPad; +if scanStartSamp<1 + scanStartSamp=1; +end +scanEndSamp=indxBlockOff(end)+defEndPad; +if scanEndSamp>Nsamples, + scanEndSamp=Nsamples; +end +%====================================================== +%======================================================================= + %% GET BASIC TRIALINFO FOR STIM SEQUENCE + +totalStimInfo=[]; +countStims=1; +curBlockNum=0; +for iEv=1:NeventsPP + + if ismember(eventPPInfo(iEv,1),[1 4]), + curBlockNum=curBlockNum+1; + curStimSeq=1; + elseif eventPPInfo(iEv,1)==2, + if eventPPInfo(iEv+1,1)~=3 + error('Image onset is not followed by an image offset'); + end + tmpInfo=[runNumber,... %1. + curBlockNum ,... %2. + nan,... %3. % here should be an index refereing to the actual image used + eventPPInfo(iEv,2),... %4. + eventPPInfo(iEv,3),... %5. + eventPPInfo(iEv,4),... %6. + eventPPInfo(iEv,6),... %7. + eventPPInfo(iEv+1,6)-1,... %8. + curStimSeq]; %9. %not used. just added for compatibility with glasgow data; Maybe here could be used the sequence of image in the block + + totalStimInfo=[totalStimInfo; tmpInfo]; + curStimSeq=curStimSeq+1; + end + + +end +%============================================== +%% Get Response characteristics +totalStimInfo(:,10:39)=0; + +Nstims=size(totalStimInfo,1); +for iStim=1:Nstims, + indStart=totalStimInfo(iStim,7); + indEnd=totalStimInfo(iStim,8); + indTotalEnd=Nsamples; + tmpResp=resp(indStart:indEnd); + tmpResp2End=resp(indStart:indTotalEnd); + tmpDifResp=diff(tmpResp); + tmpDifResp2End=diff(tmpResp2End); + [i1,j1]=find(tmpResp==256); + [k1,l1]=find(tmpResp==512); + + if (tmpResp(1)==256)|(tmpResp(1)==512) + isPressedOnOnset=1; + else + isPressedOnOnset=0; + end + + %----------------------------------------------------- + if (~isempty(i1))&(~isempty(k1)), + isPressed=1; + isDoubleResponse=1; + pressedCode=nan; + isCorrect=nan; + isLureAsCorrect=nan; + respTime=nan; + respDur=nan; + isPressedLate=nan; + + elseif (isempty(i1))&(isempty(k1)), + isPressed=0; + isDoubleResponse=0; + pressedCode=nan; + isCorrect=nan; + isLureAsCorrect=nan; + respTime=nan; + respDur=nan; + + %======================================= + % Check for late response after the stim is off and before the next + % stim is on + if iStim1 + isCorrect=1; + isLureAsCorrect=0; + elseif totalStimInfo(iStim,6)==1 + isCorrect=0; + isLureAsCorrect=0; + else + isCorrect=0; + isLureAsCorrect=0; + end + respTime=l1(1)*(1./hdr.Fs); + [d1,d2]=find(tmpDifResp(k1(1):end)<0,1,'first'); + if isempty(d1), + [d1,d2]=find(tmpDifResp2End(k1(1):end)<0,1,'first'); + end + respDur=d2*(1./hdr.Fs); + isPressedLate=0; + end + %----------------------------------------------------- + totalStimInfo(iStim,10)=isPressed; + totalStimInfo(iStim,11)=isPressedLate; + totalStimInfo(iStim,12)=isDoubleResponse; + totalStimInfo(iStim,13)=pressedCode; + totalStimInfo(iStim,14)=isCorrect; + totalStimInfo(iStim,15)=isLureAsCorrect; + totalStimInfo(iStim,16)=respTime; + totalStimInfo(iStim,17)=respDur; + + totalStimInfo(iStim,39)=isPressedOnOnset; % New field to check if the button is pressed when the stim comes on; Appended to the end of the original list + +end + + + +%========================================== +%% Mark the first and the last in Block +for iBlock=1:Nblocks + [i1,j1]=find(totalStimInfo(:,2)==iBlock); + if ~isempty(i1) + totalStimInfo(i1(1),18)=1; + totalStimInfo(i1(end),19)=1; + end +end + + +%=========================================== +%% Append trialinfo for previous trial +pastTrialInfo=nan(size(totalStimInfo,1),19); +pastTrialInfo(2:end,:)=totalStimInfo(1:end-1,1:19); +totalStimInfo(:,20:38)=pastTrialInfo; + +% --- assign memory and image types of previous block. This is because +% witin each block the memory and image type is always the same. So by +% knowinn the memory and image type of the current , also the ones for the +% previous are known. What can be usefull information is what was the +% memory and image type of the preceding block within the run. +for iBlock=2:Nblocks + [i1,j1]=find(totalStimInfo(:,2)==iBlock); + if ~isempty(i1) + totalStimInfo(i1,23)=blockPPInfo(iBlock-1,2); + totalStimInfo(i1,24)=blockPPInfo(iBlock-1,3); + end +end + +%==================================================== +%==================================================== +%==================================================== +%==================================================== +%==================================================== +%==================================================== +%============================================================== +%============================================================== +%============================================================== +%% Fixation BLocks +% FIXATIONBLOCKINFO +% Columns: +% 1. Previous block ImgType +% 2. Previous block memoryType +% 3. Next block ImgType +% 4. Next block blockType +% 5. Fixation Start Sample +% 6. Fixation End Sample + +totalBlockFixInfo=[]; +for iBlock=1:Nblocks, + + if blockPPInfo(iBlock,1)==4, + + + if iBlock>1, + prevInfo= blockPPInfo(iBlock-1,[2:3]); + else + prevInfo=[nan nan]; + end + if iBlockNsamples); + if ~isempty(i1) + psFixTrialInfo(i1,:)=[]; + end + [i1,j1]=find(psFixTrialInfo(:,1)<1); + if ~isempty(i1) + psFixTrialInfo(i1,:)=[]; + end +%================================================== + +%============================================================ +%% ------------CREATE OUTPUT +%========================================================================= +trl=[]; +if strcmp(cutMode,'trials'), +outTrialInfo=totalStimInfo; +if strcmp(lockedOn,'Image') + trlStims=[outTrialInfo(:,7)-preStimSamples outTrialInfo(:,7)+postStimSamples -repmat(preStimSamples,size(outTrialInfo,1),1) outTrialInfo]; + trl=[trlStims; psFixTrialInfo]; +elseif strcmp(lockedOn,'Response') + outTrialInfo=outTrialInfo((logical(outTrialInfo(:,10)))&(~logical(outTrialInfo(:,12)))&(~logical(outTrialInfo(:,39))),:); + trlStims=[outTrialInfo(:,7)+floor(hdr.Fs*outTrialInfo(:,16))-preStimSamples outTrialInfo(:,7)+floor(hdr.Fs*outTrialInfo(:,16))+postStimSamples -repmat(preStimSamples,size(outTrialInfo,1),1) outTrialInfo]; + trl=[trlStims; psFixTrialInfo]; +end + +trlInfoColDescr=trlColDescr; +%=================================================================== +elseif strcmp(cutMode,'blocks'), + + outTrialInfo=totalBlockStimInfo; + trl=[outTrialInfo(:,6)-preStimSamples outTrialInfo(:,7)+postStimSamples -repmat(preStimSamples,size(outTrialInfo,1),1) outTrialInfo]; + +trlInfoColDescr=blockColDescr; + +elseif strcmp(cutMode,'fixation'), + outTrialInfo=totalBlockFixInfo; + trl=[outTrialInfo(:,5)-preStimSamples outTrialInfo(:,6)+postStimSamples -repmat(preStimSamples,size(outTrialInfo,1),1) outTrialInfo]; +trlInfoColDescr={''}; +end + + + +%=================================================================== + +trialSummary=[]; +%=========================================== +%--- Compute summary of trial information to print is summary file +trialSummary.Nblocks=NstimBlocks+NfixBlocks; +trialSummary.Nblocks_Fixation=NfixBlocks; +trialSummary.Nblocks_0Back=length(find(totalBlockStimInfo(:,3)==1)); +trialSummary.Nblocks_2Back=length(find(totalBlockStimInfo(:,3)==2)); +trialSummary.Nblocks_faces=length(find(totalBlockStimInfo(:,2)==1)); +trialSummary.Nblocks_tools=length(find(totalBlockStimInfo(:,2)==2)); +trialSummary.Ntrials=size(totalStimInfo,1); +trialSummary.Ntrials_0Back=length(find(totalStimInfo(:,5)==1)); +trialSummary.Ntrials_2Back=length(find(totalStimInfo(:,5)==2)); +trialSummary.Ntrials_faces=length(find(totalStimInfo(:,4)==1)); +trialSummary.Ntrials_tools=length(find(totalStimInfo(:,4)==2)); +trialSummary.NtrialsRESP=length(find((totalStimInfo(:,10)==1))); +trialSummary.NtrialsRESP_0Back=length(find((totalStimInfo(:,5)==1)&(totalStimInfo(:,10)==1))); +trialSummary.NtrialsRESP_2Back=length(find((totalStimInfo(:,5)==2)&(totalStimInfo(:,10)==1))); +trialSummary.NtrialsRESP_faces=length(find((totalStimInfo(:,4)==1)&(totalStimInfo(:,10)==1))); +trialSummary.NtrialsRESP_tools=length(find((totalStimInfo(:,4)==2)&(totalStimInfo(:,10)==1))); +%=========================================== +%=================================================================== + + + + +end